DEV Community

Cover image for Sveltekit and Appwrite Cloud: How to build a list app
oteri
oteri

Posted on • Originally published at hackernoon.com

Sveltekit and Appwrite Cloud: How to build a list app

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 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 leveraging Appwrite, Svelte, and Tailwind CSS for styling.

Source Code

The complete source code for this project is in this GitHub repo.

Demo

To try out the live demo, check it out here.

demo app

Prerequisites

Throughout this tutorial, you will need the following:

  • Have the JavaScript runtime, Node.js, installed on your local machine
  • Basic JavaScript knowledge and understanding of Svelte
  • An Appwrite Cloud account. Sign-up 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

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

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

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

adding attributes

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

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

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

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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.

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

    <script>
      export let list;
    </script>

    <button type="button">
      {list}
    </button>
Enter fullscreen mode Exit fullscreen mode

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

    <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>
Enter fullscreen mode Exit fullscreen mode

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

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"
Enter fullscreen mode Exit fullscreen mode

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

    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);
Enter fullscreen mode Exit fullscreen mode

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

    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);
Enter fullscreen mode Exit fullscreen mode

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

    import { writable } from "svelte/store";

    export const data = writable({
      name: "",
    });
Enter fullscreen mode Exit fullscreen mode

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

    <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>
Enter fullscreen mode Exit fullscreen mode

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

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

    <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>
Enter fullscreen mode Exit fullscreen mode

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:

Over in the ItemList component, update the file:

src/lib/ItemList.svelte

    <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>
Enter fullscreen mode Exit fullscreen mode

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 today, and you will keep coming back for more, just like this demonstration list app.

Resources

Top comments (0)