Sveltekit and Appwrite Cloud: How to build a list app
Tags: svelte, appwrite, javascript, tutorial
Published: 2023-08-03 08:01:26

Creating reliable and practical applications is of the utmost importance in modern web development. Frameworks and tools that not only speed up the development process but also provide better functionality and performance are constantly sought after by developers. In this situation, the pairing of Appwrite and Svelte stands out as a complete toolkit that enables programmers to create feature-rich web applications quickly.

Appwrite, an open-source backend as a service (BaaS) platform, provides developers with many services like managing user authentication, data storage, file storage, and more with its intuitive APIs that abstract complex backend operations, allowing developers to focus on building scalable frontend applications.

Svelte is a free and open-source frontend component framework used to build user interfaces by compiling components at build time without needing a [virtual DOM](https://svelte.dev/blog/virtual-dom-is-pure-overhead) resulting in faster rendering.

In this article, you will learn how to use Appwrite’s API to create a list app with features like task creation and deletion and storing the data in [Appwrite Cloud](https://cloud.appwrite.io/) leveraging Appwrite, Svelte, and [Tailwind CSS](https://tailwindcss.com/) for styling.


## Source Code

The complete source code for this project is in this [GitHub repo](https://github.com/Terieyenike/appwrite-svelte-list-app). 


## Demo

To try out the live demo, check it [out here](https://svelte-list-app.vercel.app/).


![demo app](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688203767489_image.png)



## Prerequisites

Throughout this tutorial, you will need the following:

- Have the JavaScript runtime, [Node.js](https://nodejs.org/), installed on your local machine
- Basic JavaScript knowledge and understanding of Svelte
- An Appwrite Cloud account. [Sign-up](https://cloud.appwrite.io/login) is free


## Setting up Appwrite Cloud

With Appwrite Cloud, you do not need to run a local instance on your system using Docker. Everything happens in the cloud. 

For setup, go to your Appwrite Cloud admin console and create a new project by clicking the **+ Create project** button. 


![create project](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688135382741_image.png)


**Creating a database**
The database section allows you to edit, create, and view your collections. Navigating into the completed project, click the Databases tab on the console's left pane and create a new database.
 

![create a database](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688136303687_Screenshot+2023-06-30+at+15.42.17.jpg)


**Creating collections**
Once you have created the database, it is time to add a new collection to your project. Under the **Collections** tab, click the **+ Create collection** button and give it a name.


![collections](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688136811931_image.png)


Navigate into the **Collections** to define and create your data structure by adding attributes. 


![adding attributes](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688137131901_Screenshot+2023-06-30+at+15.56.59.jpg)


Here’s what your attributes should look like:


| Attribute Key | Attribute type | Size | Default value | Required |
| ------------- | -------------- | ---- | ------------- | -------- |
| item          | string         | 255  | -             | Yes      |

Still in the **Collections** menu, under the **Settings** tab, change the read and write permissions and the delete permission for your collection.


![roles and permissions](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688138490144_Screenshot+2023-06-30+at+16.20.05.jpg)



## Adding platforms to your project

This step is vital as it would validate the requests coming from the client side into the Appwrite Cloud console.

On the console home page, click on the **Overview** tab and scroll to the section **Integrations**. Click on the Add platform and select one of the options from the drop-down.

**PS**: For this application, select the **Web App** option.


![web app platform](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688140076997_Screenshot+2023-06-30+at+16.46.32.jpg)


**Web**
To add the web platform, give it a **Name** and **Host**. The **Name** can be anything you desire, and the **Host** can be the domain under which the web project has access during development. The **Host** can use the asterisk (*****) symbol to locally test the web app.


![add platform](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688141759062_Screenshot+2023-06-30+at+17.15.50.jpg)



## Creating a new Svelte Project

To create a new Svelte project, run this command in the command line interface (CLI) to scaffold the app.

```
    npm create svelte@latest list-app
```

Follow the instructions on the terminal as described below:

- Accept the installation with the `y` flag
- Select the **Skeleton project** from the Svelte app template
- For type checking with TypeScript, choose **No**. If otherwise, and you are comfortable using TS, select it
- The last option is optional if you decide to add it to the project

Use the following command below to begin the next steps:

```
    cd listsApp
    
    npm install (or pnpm install, etc)
    
    git init && git add -A && git commit -m "Initial commit" (optional)
    
    npm run dev
```

The command above does the following:

- `cd`: navigate to the created Svelte project directory
- The second command installs all the dependencies using the package manager, `npm`
- The `git` commands initialize, stage, and save a snapshot of the current project state and keep track of your changes
- Start the development server on port `5173` to preview the app


## Installation

**Appwrite SDK**
Launch your terminal and type the following command to install the Appwrite Web SDK to your project:

```
    npm install appwrite
```

**Tailwind CSS**
Tailwind is a utility-first CSS framework packed with classes directly in your markup. The best way to install Tailwind CSS onto your project is by using the Tailwind CLI tool. Check out the Sveltekit framework guide and follow [this process](https://tailwindcss.com/docs/guides/sveltekit).


## Creating the user interface

The UI for the list app will display all the data from the documents in Appwrite Cloud and can delete an item with a click.

Create a new folder called **lib** in the **src** directory. Next, create a new file named, `ItemList.svelte`. 

Now, update these components `ItemList.svelte` and `+page.svelte` with the following code:

`src/lib/ItemList.svelte`
```js
    <script>
      export let list;
    </script>
    
    <button type="button">
      {list}
    </button>
```

The above code snippets do the following:

- The **script** section uses the export let syntax used to declare a named export in a component which exposes the variable list accessible in the `ItemList` component
- Pass the variable list in the **button** element

`src/routes/+page.svelte`
```js
    <script>
      import ItemList from "../lib/ItemList.svelte";
    </script>
    
    <div
      class="w-4/5 mx-auto max-w-6xl flex flex-col lg:w-6/12 select-none min-h-screen"
    >
      <p class="text-center my-6">Delete an item with a click.</p>
      <img
        src="assets/app-launch.svg"
        alt="Launch rocket"
        class="mx-auto w-3/4 lg:w-2/4"
      />
      <input
        type="text"
        placeholder="enter item"
        class="bg-gray-200 p-4 border-0 text-slate-600 rounded-lg text-center text-xl my-3"
      />
      <ul class="flex p-0 flex-wrap gap-3 mt-4">
        <li
          class="md:text-4xl lg:text-2xl bg-slate-100 p-5 rounded-lg grow text-center shadow hover:bg-orange-200 cursor-pointer"
        >
          <ItemList list={"Watch the superbowl half-time show"} />
        </li>
      </ul>
      <footer class="mt-auto">
        <div class="mt-10 text-center text-gray-500">
          <address>
            Built by
            <span class="text-blue-600">
              <a href="https://twitter.com/terieyenike" target="_">Teri</a>
            </span>
            &copy; 2023
          </address>
          <div>
            <p>
              Fork, clone, and star this
              <a
                href="https://github.com/Terieyenike/appwrite-svelte-list-app"
                target="_"
                rel="noopener noreferrer"
                class="text-blue-600">repo</a
              >
            </p>
          </div>
          <p class="text-sm">Appwrite x Svelte x Tailwind CSS</p>
        </div>
      </footer>
    </div>
```

In the code snippets above, the following occurs:

- Import the component, `ItemList`
- Throughout the markup, Tailwind CSS classes beautify the UI 
- For the image, use any image of your choice saved in the **static** folder inside the **assets** folder as well
- Declare the **props** value as `list` in the `ItemList` component

Here’s what you should see:


![preview list app](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688160480557_image.png)


PS: So far, the app is static.


## Creating environment variables

Environment variables are dynamic values set in a program, operating system (OS), or application accessible in a runtime environment. They keep sensitive information away from the application code.

Create a new file, `.env`, in the root of the project directory and copy-paste this code:

`.env`
```
    PUBLIC_DATABASE_ID="<DATABASE_ID>"
    PUBLIC_COLLECTION_ID="<COLLECTION_ID>"
    PUBLIC_PROJECT_ID="<PROJECT_ID>"
    PUBLIC_INSTANCE_URL="https://cloud.appwrite.io/v1"
```

Replace all the values in the quotation marks with the actual values from your Appwrite Cloud admin console.


## Connecting to Appwrite backend

**Initializing the Appwrite client**
Let’s create a file named `utils.js` in the **src** directory. Copy-paste this code:

`utils.js`
```js
    import {
      PUBLIC_DATABASE_ID,
      PUBLIC_COLLECTION_ID,
      PUBLIC_PROJECT_ID,
      PUBLIC_INSTANCE_URL,
    } from "$env/static/public";
    import { Client, Databases, ID, Query } from "appwrite";
    
    const client = new Client();
    
    const databases = new Databases(client);
    
    client.setEndpoint(PUBLIC_INSTANCE_URL).setProject(PUBLIC_PROJECT_ID);
    
    export const create = (data) =>
      databases.createDocument(
        PUBLIC_DATABASE_ID,
        PUBLIC_COLLECTION_ID,
        ID.unique(),
        data
      );
    
    export const getList = databases.listDocuments(
      PUBLIC_DATABASE_ID,
      PUBLIC_COLLECTION_ID,
    [Query.orderDesc("$createdAt")]
    
    );
    
    export const deleteList = (database_id, collection_id, data) =>
      databases.deleteDocument(database_id, collection_id, data);
```

The code above won’t allow your application to run in development. But when you want to push your project code to GitHub, use this instead and place the `.env` file in the `.gitignore` file.

For now, let’s use this method with the code instead:

`utils.js`
```js
    import { Client, Databases, ID, Query } from "appwrite";
    
    const client = new Client();
    
    const databases = new Databases(client);
    
    client
      .setEndpoint("https://cloud.appwrite.io/v1")
      .setProject("PROJECT_ID");
    
    export const create = (data) =>
      databases.createDocument(
        "[DATABASE_ID]",
        "[COLLECTION_ID]",
        ID.unique(),
        data
      );
    
    export const getList = databases.listDocuments(
      "[DATABASE_ID]",
      "[COLLECTION_ID]",
      [Query.orderDesc("$createdAt")]
    );
    
    export const deleteList = (database_id, collection_id, data) =>
      databases.deleteDocument(database_id, collection_id, data);
```

The code above does the following:

- Import the Appwrite package and initialize the new instance of the web SDK
- Pass the **client** with the endpoint and project details
- Export the **create** function with the **data** parameter, responsible for creating the document listing all the document data on the client side. This request generates a unique **ID**
- Export the **getList** function, which uses the list documents databases API with the database ID and collection ID, as well as utilizing the query endpoint to show the recent item first when added by a user
- Finally, the **deleteList** function deletes a document by its unique **ID** with the data parameter 


## Creating data using Appwrite APIs

Since this is a CRUD application, let’s handle sending a new item to Appwrite Cloud. Before updating the `+page.svelte` page, create another file named `store.js` in the **src** directory.

Copy-paste this code:
`src/store.js`
```js
    import { writable } from "svelte/store";
    
    export const data = writable({
      name: "",
    });
```

In Svelte, the writable function is a built-in store creator that enables you to create a writable store with values from **outside** a component.

Now that is taken care of, update the `+page.svelte` components:

`src/routes/+page.svelte`
```js
    <script>
      import { create } from "../utils";
      import { data } from "../store";
      ...
    
      function addToList(e) {
        if (!$data.name) {
          return;
        } else if (e.key !== "Enter") {
          return;
        }
        create({
          item: $data.name,
        }).then(
          function (response) {
            window.location.reload();
          },
          function (error) {
            console.log(error);
          }
        );
        $data.name = "";
      }
    </script>
    
    <div
      class="w-4/5 mx-auto max-w-6xl flex flex-col lg:w-6/12 select-none min-h-screen"
    >
      <p class="text-center my-6">Delete an item with a click.</p>
      <!-- img src element -->
      <input
        type="text"
        placeholder="enter item"
        on:keydown={addToList}
        bind:value={$data.name}
        class="bg-gray-200 p-4 border-0 text-slate-600 rounded-lg text-center text-xl my-3"
      />
      <ul class="flex p-0 flex-wrap gap-3 mt-4">
        <li
          class="md:text-4xl lg:text-2xl bg-slate-100 p-5 rounded-lg grow text-center shadow hover:bg-orange-200 cursor-pointer"
        >
          <ItemList list={"Watch the superbowl half-time show"} />
        </li>
      </ul>
      <!-- Footer element -->
    </div>
```

At this point, the code above does the following:

- Import the `create` function as well as the **store** data
- In the `addToList` function, nothing happens if no value is present in the input field. Also, with the `create` function, the key **item** reads the value, `$data.name`, which is saved in Appwrite if successful. After that, the page reloads immediately.
- The input element with the action **keydown** event that listens to the enter key on the keyboard and binds the empty value

Let’s try sending an item to Appwrite Cloud. Head over to your Appwrite console to confirm the value is stored. 


![stored data in the console](https://paper-attachments.dropboxusercontent.com/s_DCD323DA163D8480515F2C4A4502C20461B2F8882C7D2ADAE7B66BDCAEEA7161_1688169894676_image.png)



## Displaying the list items

This section displays the list of items stored in Appwrite on the client side. Again, update the code in the `+page.svelte` component.

`src/routes/+page.svelte`
```js
    <script>
      import { onMount } from "svelte";
      import { getList, create } from "../utils";
      ...
    
      let lists = [];
    
      onMount(() => {
        getList.then(
          function (response) {
            lists = response.documents;
          },
          function (error) {
            console.log(error);
          }
        );
      });
    
      // addToList function
    
    </script>
    <div
      class="w-4/5 mx-auto max-w-6xl flex flex-col lg:w-6/12 select-none min-h-screen"
    >
      <!-- p, img, input element -->
     
      <ul class="flex p-0 flex-wrap gap-3 mt-4">
        {#if lists.length == 0}
          <p class="m-auto mt-8">No data in the database</p>
        {:else}
          {#each lists as list (list.$id)}
            <li
              class="md:text-4xl lg:text-2xl bg-slate-100 p-5 rounded-lg grow text-center shadow hover:bg-orange-200 cursor-pointer"
            >
              <ItemList {list} />
            </li>
          {/each}
        {/if}
      </ul>
      <!-- Footer element -->
    </div>
```

In the code snippet above, the following occurs:

- Import the `onMount` lifecycle function as well as the getList function
- Declare an empty array with the variable **lists**
- The `onMount` function is called immediately when a component is first rendered and inserted into the DOM
- Since it is an asynchronous task, the `then()` method returns a **promise**
- In the `<ul>` element, loop through the **lists** array using the `#each` expression
- Pass the props, **list** in the `ItemList` component

View the entire code for the `+page.svelte` file in this [gist](https://gist.github.com/Terieyenike/f722b0861f6d97460b66eee0fefd6399):

Over in the **ItemList** component, update the file:

`src/lib/ItemList.svelte`
```js
    <script>
      import { deleteList } from "../utils";
    
      ...
    
      function handleDeleteListItem() {
        deleteList(list.$databaseId, list.$collectionId, list.$id).then(
          function (response) {
            console.log(`${list.item} successfully deleted`);
            window.location.reload();
          },
          function (error) {
            console.log(error);
          }
        );
      }
    </script>
    
    <button type="button" on:click={handleDeleteListItem}>
      {list.item}
    </button>
```

In this scenario, the code above does this:

- Import the `deleteList` function 
- The `handleDeleteListItem` function will delete an item from the list array on click of a button
- The page reloads when the action occurs with the `reload()` method
- Before now, in the button element, the text was not dynamic; replaced with the passed props


## Wrapping it up

Building a list app using the powerful combination of Svelte and Appwrite brings together the best in frontend development and backend services. In conclusion, the connection between Svelte and Appwrite enables developers to build web applications efficiently with enhanced performance and robust backend functionality.

Try using [Appwrite Cloud](https://cloud.appwrite.io/) today, and you will keep coming back for more, just like this [demonstration list app](https://svelte-list-app.vercel.app/). 


## Resources
- [Using Environment Variables With SvelteKit](https://joyofcode.xyz/sveltekit-environment-variables)
- [Appwrite’s Databases APIs](https://appwrite.io/docs/client/databases)
- [Svelte/Store writable function](https://svelte.dev/docs/svelte-store#writable)