How to use the different content types in Appwrite functions
Tags: appwrite, serverlessfunction, flutter, mobile
Published: 2023-10-24 08:24:23

The technology landscape is constantly changing with new and innovative solutions. This has allowed the industry to utilize modern engineering solutions such as Docker, Kubernetes, managed services, and serverless functions when building and deploying software.

In fact, serverless functions have leveled the playing field by giving developers the required flexibility to build and ship software fast at a reduced cost and with good developer experience.

In this post, we will explore how Appwrite is changing the game through its serverless function offering and support for multiple content types; we’ll also demonstrate how to use it in a Flutter application.

## GitHub links

The project source codes are below:

-   [App](https://github.com/Mr-Malomz/content_function)[write function with Dart](https://github.com/Mr-Malomz/appwrite_content_type)
-   [Flutter App](https://github.com/Mr-Malomz/flutter_appwrite_content/tree/dev)

## Prerequisites

To fully grasp the concepts presented in this tutorial, the following are required:

-   Basic understanding of Dart and Flutter
-   GitHub [account](https://github.com/signup?ref_cta=Sign+up&ref_loc=header+logged+out&ref_page=%2F&source=header-home)
-   Appwrite Cloud [account](https://cloud.appwrite.io/register?utm_source=hackmamba&utm_medium=hackmamba-blog). Sign-up is free.

## Serverless functions on Appwrite

Appwrite provides a unique approach to managing and hosting serverless functions within either a secure cloud or self-hosted environment, offering an excellent user experience for developers. In addition, it also lets developers:

-   Customize and extend code capabilities
-   Simplify deployment complexities
-   Effectively address scalability concerns

Beyond its extensibility, security features, and other valuable capabilities, Appwrite also facilitates the return of various content types as a response, such as JSON, HTML, Text, and more. This capability is transformative and a game-changer because it allows developers to extend their use cases and unlock new possibilities.

Below are the content types supported by Appwrite and the sample use case:

**Empty**

This response type in a serverless function does not yield any output when used in a function. A sample use case is a function responsible for initializing the database and corresponding collections.

```dart
import 'dart:async';

Future<dynamic> main(final context) async {
  //call to another function can come here
  return context.res.empty();
}
```

**Redirect**

This type is used to redirect users to another URL. A sample use case is a function that redirects users to a specified URL.

```dart
import 'dart:async';
import 'dart:convert';

Future<dynamic> main(final context) async {
  final data = jsonDecode(context.req.body);
  return context.res.redirect(data["url"], 301);
}
```

**JSON**

This type returns a JSON. A sample use case is a function that uploads files into storage and returns the upload information.

```dart
import 'dart:async';

Future<dynamic> main(final context) async {
  //upload to storage code
  return context.res.json({"upload_id" "erjkdkkSAMPLE_UPLOAD_2343ujej"});
}
```

**HTML**

This type returns HTML content. A sample use case is a function designed to manage an RSVP form.

```dart
import 'dart:async';

Future<dynamic> main(final context) async {
  final html = '''<!doctype html>
      <html lang="en">
      <head>
        <meta charset="utf-8">
        <title>Contact Form</title>
      </head>
      <body>
        <form action="/" method="POST">
          <input type="email" id="email" name="email" placeholder="Email" required>
          <button type="submit">Submit</button>
        </form>
      </body>
      </html>
      ''';

  return context.res.send(html, 200, {'content-type': 'text/html'});
}
```

**Text**

This type returns a text. A sample use case is a function designed to verify the operational status of a service.

```dart
import 'dart:async';

Future<dynamic> main(final context) async {
  //api check to a service
  return context.res.send("The service is running!");
}
```

Now that we understand Appwrite’s serverless function and its support for multiple content types, we can build our application and test multiple responses.

## Create Appwrite’s serverless function to get multiple content types

**Set up a project on Appwrite**

To get started, we need to create a project on Appwrite’s console to deploy and test our function. To do this, log into the Appwrite console, click the **Create project** button, input `appwrite_content_type` as the name, and **Create**.

![Create project](https://paper-attachments.dropboxusercontent.com/s_C9210607DAFF6D80615947ED6BE237716E98BADEF0416B60FACF081B1CAEFB80_1687518026974_Screenshot+2023-06-23+at+11.38.55.png)

Secondly, we must navigate to the **Functions** tab and create a function using the `Dart` template.

![Create using dart template](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630209779_Screenshot+2023-10-18+at+12.08.36.png)

Input `appwrite_content_type` as the name and continue.

![Create function name](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630485884_Screenshot+2023-10-18+at+12.09.40.png)

Check the **Generate API key on completion** option so that our function can securely connect to Appwrite and proceed.

![Set variables](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630523540_Screenshot+2023-10-18+at+12.25.35.png)

Check the **Create a new repository** option and connect to GitHub by following the prompt.

![repo type](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630710785_Screenshot+2023-10-18+at+12.25.56.png)
![Connect to GitHub](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630710824_Screenshot+2023-10-18+at+12.26.26.png)

On connection, input `appwrite_content_type` as the repository name, accept the default branch suggestion and **Create**.

![Repository name](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630815791_Screenshot+2023-10-18+at+12.29.19.png)
![Accept branch details](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697630815834_Screenshot+2023-10-18+at+12.30.13.png)

With that done, we should see our deployed function on Appwrite and the corresponding source code on GitHub.

![source code](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697631402766_Screenshot+2023-10-18+at+12.42.10.png)
![Deployed on Appwrite](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697631402813_Screenshot+2023-10-18+at+13.12.29.png)

**Modify the generated source code on GitHub**

Next, we must modify the generated function source code on GitHub by navigating to the `lib/main.dart` file, click the **Edit** icon, and add the snippet below:

![Edit main.dart](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697631694700_Screenshot+2023-10-18+at+12.45.01.png)

```dart
import 'dart:async';
import 'dart:convert';

Future<dynamic> main(final context) async {
  final data = context.req.headers;
  final responseType = json.encode(data['Content-Type']);

  switch (responseType) {
      case 'application/json':
        return context.res.json(
            {'type': 'This is a sample JSON response from Appwrite function'});
      case 'text/html':
        return context.res.send(
            '<h1>This is a sample HTML response from Appwrite function</h1>',
            200,
            {'content-type': 'text/html'});
      default:
        return context.res
            .send('This is a sample text response from Appwrite function');
    }
}
```

The snippet above gets the request header based on a header and conditionally returns a matching response type.

Lastly, we must commit the changes by clicking the **Commit changes…** button, input appropriate commit message, and save.

![Commit changes](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697631863603_Screenshot+2023-10-18+at+12.45.58.png)

With that done, Appwrite will automatically redeploy our function using the updated source code.

![Latest deployment](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1697631997513_Screenshot+2023-10-18+at+12.47.41.png)

## Leveraging deployed function in a Flutter application

To get started, we need to clone the project by navigating to the desired directory and running the command below:

```bash
git clone https://github.com/Mr-Malomz/flutter_appwrite_content
```

**Running the project**

We need to install the project dependencies by running the command below:

```bash
flutter pub get
```

Then, run the project using the following command:

```
flutter run
```

The command above will run the application on the selected device.

![Home screen](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1695428703601_Screenshot+2023-09-22+at+00.07.07.png)

**Add platform support and update function permission**

To securely connect our Flutter application with Appwrite, we need to add it as a supported platform on the console. To do this, navigate to the **Overview** menu and click the **Flutter** button.

![Add platform](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1695477569176_Screenshot+2023-09-23+at+14.48.18.png)

Next, we must modify the Flutter application as detailed below:

To obtain our Bundle ID, navigate to the path below:
**ios > Runner.xcodeproj > project.pbxproj**

Open the `project.pbxproj` file and search for `PRODUCT_BUNDLE_IDENTIFIER`.

![IOS Flutter app](https://paper-attachments.dropboxusercontent.com/s_9813CFC6C5B2163D17C6B733CCD2F7F4E13D8D4D744ED2740D99BCB62D05C011_1690504111962_Screenshot+2023-07-27+at+00.41.21.png)

To obtain our package name for Android, navigate to the path below:
**android > app > src > debug > AndroidManifest.xml**

Open the `AndroidManifest.xml` file and copy the `package` value.

![Android Flutter App](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1695496598562_Screenshot+2023-09-23+at+20.15.06.png)

We must also modify the `AndroidManifext.xml` as shown below:

```xml
<manifest>
  <application>
    <activity android:name="com.linusu.flutter_web_auth_2.CallbackActivity" android:exported="true">
      <intent-filter android:label="flutter_web_auth_2">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="appwrite-callback-[PROJECT_ID]" />
      </intent-filter>
    </activity>
  </application>
</manifest>
```

We must also replace the `[PROJECT_ID]` section with our actual Appwrite project ID. We can get our project ID from the **Overview** tab.

![Project ID](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1695713600516_Screenshot+2023-09-26+at+08.32.12.png)

Lastly, we must update the deployed function permission to use it in our Flutter application. To do this, navigate to the **Settings** tab of the deployed function, scroll to the **Execute Access** section, select `Any`, and **Update**.

![function permission](https://paper-attachments.dropboxusercontent.com/s_2502D8AC4856C2EA82A5DE9E8EA607825C32DB7EE286D94E4E7740A4458B34F3_1695497501686_Screenshot+2023-09-17+at+00.50.03.png)

**PS**: _Appwrite ships with a robust role and access management to cater to individual and company needs._

**Create a service**

With that done, we need to create a service file to separate the application core logic from the UI. To do this, create a `utils.dart` file inside the `lib` directory. Then, add the snippet below:

```dart
import 'package:appwrite/appwrite.dart';

class _AppConstant {
  final String endpoint = "https://cloud.appwrite.io/v1";
  final String projectId = "REPLACE WITH PROJECT ID";
  final String functionId = "REPLACE WITH FUNCTION ID";
}

class ContentService {
  Client _client = Client();
  ContentService() {
    _init();
  }

  //initialize the application
  _init() async {
    _client
        .setEndpoint(_AppConstant().endpoint)
        .setProject(_AppConstant().projectId);

    //get current session
    Account account = Account(_client);

    try {
      await account.get();
    } on AppwriteException catch (e) {
      if (e.code == 401) {
        account
            .createAnonymousSession()
            .then((value) => value)
            .catchError((e) => e);
      }
    }
  }

  Future getContentType(String selectedType) async {
    Map<String, String> contentTypes = {
      "text": "text/plain",
      "json": "application/json",
      "html": "text/html"
    };
    Functions functions = Functions(_client);
    try {
      var result = await functions.createExecution(
        functionId: _AppConstant().functionId,
        headers: {'Content-Type': contentTypes[selectedType]},
      );
      return result.responseBody;
    } catch (e) {
      throw Exception('Error creating subscription');
    }
  }
}
```

The snippet above does the following:

-   Creates a private class `_AppConstant` to save required properties
-   Creates a `ContentService` class with `_client` and `_account` properties to connect to the Appwrite instance
-   Creates an `_init` method that configures Appwrite using the properties
-   Creates a `getContentType` method that uses the `_client` property to create an instance of the function and then use the `createExecution` method to trigger the function with the required parameters

**Consuming the service**

First, we need to modify the `home.dart` file inside the `screens` folder by importing the required dependencies and using the service to perform required operations:

```dart
import 'package:flutter/material.dart';
import 'package:flutter_appwrite_content/utils.dart';
class Home extends StatefulWidget {
  const Home({super.key});
  @override
  State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
  final _formKey = GlobalKey<FormState>();
  var _selected = '';
  var _dropdownItems = ["text", "json", "html"];
  bool _isLoading = false;
  var _contentValue = '';

  _handleContentType() {
    setState(() {
      _isLoading = true;
      _contentValue = '';
    });
    ContentService().getContentType(_selected).then((value) {
      setState(() {
        _isLoading = false;
        _contentValue = value.toString();
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Content retrieved successfully!')),
      );
    }).catchError((e) {
      setState(() {
        _isLoading = false;
      });
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(content: Text('Error retrieving content!')),
      );
    });
  }

  Widget build(BuildContext context) {
    //UI code goes here
  }
}
```

The snippet above does the following:

-   Imports the required dependency
-   **Line 12-13**: Creates the `_isLoading` and `_contentValue` properties to manage application state and results from the service, respectively
-   **Line 15-36**: Creates an `_handleContentType` method that uses the `getContentType` service to get the required content

Lastly, we must update the UI to use created properties to manage the application state and display returned content.

```dart
//import goes here

class Home extends StatefulWidget {
  const Home({super.key});
  @override
  State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
  //state goes here

  _handleContentType() {
    //code goes here
  }

  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text("Appwrite Function Content"),
        backgroundColor: Colors.black,
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 30.0),
        child: Column(
          children: [
            Form(
              key: _formKey,
              child: Column(
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      //title
                      Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          const Text(
                            'Data type',
                            style: TextStyle(
                              color: Colors.grey,
                              fontSize: 14.0,
                            ),
                          ),
                          const SizedBox(height: 5.0),
                          DropdownButtonFormField(
                            items: _dropdownItems.map((String item) {
                              return DropdownMenuItem(
                                value: item,
                                child: Text(item),
                              );
                            }).toList(),
                            onChanged: (value) {
                              setState(() => _selected = value!);
                            },
                            decoration: InputDecoration(
                              contentPadding: const EdgeInsets.symmetric(
                                  vertical: 10, horizontal: 20),
                              hintText: "select data type",
                              focusedBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(10),
                                borderSide:
                                    const BorderSide(color: Colors.grey),
                              ),
                              enabledBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(10),
                                borderSide:
                                    const BorderSide(color: Colors.grey),
                              ),
                              errorBorder: OutlineInputBorder(
                                borderRadius: BorderRadius.circular(10),
                                borderSide: const BorderSide(color: Colors.red),
                              ),
                            ),
                          ),
                        ],
                      ),
                    ],
                  ),
                  const SizedBox(height: 30.0),
                  SizedBox(
                    height: 45,
                    width: double.infinity,
                    child: TextButton(
                      onPressed: () {
                        if (_formKey.currentState!.validate()) {
                          _handleContentType();
                        }
                      },
                      style: ButtonStyle(
                        backgroundColor:
                            MaterialStateProperty.all<Color>(Colors.black),
                      ),
                      child: const Text(
                        'Get content',
                        style: TextStyle(
                          color: Colors.white,
                          fontWeight: FontWeight.bold,
                          fontSize: 14.0,
                        ),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 40.0),
            Container(
              width: MediaQuery.of(context).size.width,
              height: 200.0,
              padding: EdgeInsets.all(10),
              decoration: BoxDecoration(
                border: Border.all(color: Colors.blueAccent),
                borderRadius: BorderRadius.circular(10),
              ),
              child: Text(_contentValue),
            )
          ],
        ),
      ),
    );
  }
}
```

With that done, we restart the application using the code editor or run the command below:

```bash
flutter run
```

![](https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGZzMXJ1Z3R3enhmMWxwN3V3dGtvZjZldDIweXdtdWNzdzdsODk3ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/JS8bYBEXi401OqcuoR/giphy.gif)

## Conclusion

This post discussed the support for multiple content types in Appwrite function and how to use it in a Flutter application. In addition to the use cases mentioned above, Appwrite's support for multiple content types unlocks exciting possibilities for developers by allowing them to dynamically build various types of applications.

These resources may also be helpful:

-   [Appwrite Function guide](https://appwrite.io/docs/references/cloud/client-web/functions?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Appwrite Function development guide](https://appwrite.io/docs/products/functions/development?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Appwrite Function examples](https://appwrite.io/docs/products/functions/examples?utm_source=hackmamba&utm_medium=hackmamba-blog)
-   [Appwrite support for GitHub deployment](https://appwrite.io/docs/advanced/self-hosting/functions?utm_source=hackmamba&utm_medium=hackmamba-blog)