How to Create To-Do List Apps

# Introduction

To-do list apps are typical examples of applications to learn a new framework or a new tool. They are a great way to learn something new without focusing on a complicated use case, yet having something interesting enough for people to start a real project. This tutorial will follow the long list of tutorials that you can find here and focus on the synchronization of data with a Liteflow database, HTTP and email service.

Our to-do list app will have multiple features. At the end of this tutorial, we will be able to visualize our to-dos, edit them, and delete them. All of this synchronized on a server without having to maintain any Backend application using Liteflow.

To-do App

# Create the UI and the interactions of the to-do list app

For this step, I will reuse an existing to-do application frontend and will use the one using Vuejs.

First, create the following index.html file containing the user interface (UI) and the interactions for our application (e.g., create a to-do, mark a to-do as done/undone, remove a to-do).

<!DOCTYPE html>
<html>
  <head>
    <title>Liteflow To-do</title>
    <script src="https://unpkg.com/vue"></script>
    <link
      rel="stylesheet"
      type="text/css"
      href="https://unpkg.com/todomvc-app-css@2.2.0/index.css"
    />
    <style>
      body {
        max-width: 600px;
      }
    </style>
  </head>
  <body>
    <section class="todoapp">
      <header class="header">
        <h1>Liteflow To-do</h1>
        <input
          class="new-todo"
          autofocus
          autocomplete="off"
          placeholder="What needs to be done?"
          v-model="newTodo"
          @keyup.enter="addTodo"
        />
      </header>
      <section class="main" v-if="todos.length">
        <ul class="todo-list">
          <li
            v-for="todo in todos"
            class="todo"
            :key="todo.id"
            :class="{ completed: todo.completed }"
          >
            <div class="view">
              <input
                class="toggle"
                type="checkbox"
                :checked="todo.value.completed"
                @input="e => updateTodoStatus(e, todo)"
              />
              <label>{{ todo.value.title }}</label>
              <button class="destroy" @click="removeTodo(todo)"></button>
            </div>
          </li>
        </ul>
      </section>
    </section>

    <script>
      var app = new Vue({
        data: {
          todos: [],
          newTodo: "",
        },
        methods: {
          async addTodo(todo) {},
          async removeTodo(todo) {},
          async updateTodoStatus(event, todo) {},
          async fetch() {},
        },
        mounted() {
          this.fetch();
        },
      });
      app.$mount(".todoapp");
    </script>
  </body>
</html>

You can now open your index.html file in your browser to visualize your to-do list app.

To add features in our application, we will create workflows. These workflows handle your application's business logic and can be used as an API or connected to any service's event.

We will add 5 features in our application:

# Add a to-do

The first feature we will implement is to add a new to-do and create an API for this creation of to-do using a workflow. For that, we are using the HTTP request trigger and the Database create task.

The data are stored in the todos collection based on the data sent over the API.

Let's create a create.yml file with the following:

steps:
  - type: trigger
    service: http
    eventKey: request
    endpointKey: todo-create
  - type: task
    service: database
    taskKey: create
    inputs:
      collection: todos
      value: { key: body }

And include it in our liteflow.yml file.

# liteflow.yml
workflows:
  create: ${ file('create.yml') } 

Then we can deploy this workflow using the liteflow deploy command. It will generate a unique endpoint to trigger this workflow that the to-do will use.

Once this workflow is deployed, you can update your index.html file by updating the addTodo function (line 60) with the following code:

async addTodo(todo) {
  const response = await fetch(
    "https://gateway.liteflow.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    {
      method: "POST",
      body: JSON.stringify({
        title: this.newTodo,
        completed: false,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    }
  );
  const { outputs } = await response.json();
  this.todos.push(outputs);
  this.newTodo = "";
}

The addTodo function now saves a new to-do in the database by triggering the workflow create-todo with an HTTP POST request.

You can now create to-dos and visualize them directly in the database page of your project on the console.

# List the to-dos

Your application accepts to publish new to-dos, but when you refresh the page, everything is gone. This is because it doesn't get the to-dos when the application loads. Let's fix this by creating a workflow that will query the database and return the list of to-dos in the collection todos.

Create a new file fetch-todos.yml with the following content:

steps:
  - type: trigger
    service: http
    eventKey: request
    endpointKey: todos
  - type: task
    service: database
    taskKey: list
    inputs:
      collection: todos
# liteflow.yml
workflows:
  create: ${ file('create.yml') } 
  fetch-todos: ${ file('fetch-todos.yml') }

Deploy the workflow using the command liteflow deploy to get the endpoint for this workflow.

Then, update the fetch method to get the data and populate the to-dos in the application.

async fetch() {
  const response = await fetch(
    "https://gateway.liteflow.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    { method: "POST" }
  );
  const { outputs } = await response.json();
  this.todos = outputs.values;
}

Refresh your application. All your to-dos should be loaded and you can still add new to-dos.

# Update a to-do

We now need a way to update our to-dos. We can apply the same principle as the create-todo workflow, but this time with the database update task.

Create a new file update-todo.yml with the following content:

steps:
  - type: trigger
    service: http
    eventKey: request
    endpointKey: update
  - type: task
    service: database
    taskKey: update
    inputs:
      collection: todos
      id: { key: body.id }
      value:
        completed: { key: body.value.completed }
        title: { key: body.value.title }
# liteflow.yml
workflows:
  create: ${ file('create.yml') } 
  fetch-todos: ${ file('fetch-todos.yml') }
  update: ${ file('update-todo.yml') }

Deploy the workflow using the command liteflow deploy to get the endpoint for this workflow.

You can then update your updateTodoStatus function with the endpoint given while deploying your workflow.

async updateTodoStatus(event, todo) {
  await fetch(
    "https://gateway.liteflow.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    {
      method: "POST",
      body: JSON.stringify({
        id: todo.id,
        value: {
          title: todo.value.title,
          completed: event.currentTarget.checked,
        },
      }),
      headers: {
        "Content-Type": "application/json",
      },
    }
  );
}

# Delete a to-do

Let's create our last feature accessible via the UI, deleting a to-do. Sometime when a to-do is done or was created by mistake, we need a way to delete it. We again apply the same principle using this time the Database delete task.

Create a new file delete-todo.yml with the following content:

steps:
  - type: trigger
    service: http
    eventKey: request
    endpointKey: delete
  - type: task
    service: database
    taskKey: delete
    inputs:
      collection: todos
      id: { key: body.id }
# liteflow.yml
workflows:
  create: ${ file('create.yml') } 
  fetch-todos: ${ file('fetch-todos.yml') }
  update: ${ file('update-todo.yml') }
  delete: ${ file('delete-todo.yml') }

Deploy the workflow using the command liteflow deploy to get the endpoint for this workflow.

And, finally, update our removeTodo function with the endpoint created while deploying.

async removeTodo(todo) {
  await fetch(
    "https://gateway.liteflow.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    {
      method: "POST",
      body: JSON.stringify({
        id: todo.id,
      }),
      headers: {
        "Content-Type": "application/json",
      },
    }
  );
  this.fetch();
}

Congratulation, now your application is fully functional with your UI, and all your data are stored in a private database with custom APIs to access it.

# Notify when a to-do is done

Let's now implement a notification when a to-do is done. We want to send an email to an email address every time a to-do is marked as done.

To do that, we can use the Database feature to react to any changes in the data using the onUpdated event.

The workflow will do the following: Every time a data is updated, filter to keep only the item from the todo collection, and the value of completed is true. Then, send an email using the Email send task.

Create a new file notify-todo.yml with the following content (don't forget to replace __REPLACE_WITH_YOUR_EMAIL__ with your email):

steps:
  - type: trigger
    service: database
    eventKey: onUpdated
  - type: filter
    conditions:
      - key: collection
        predicate: EQ
        value: todos
      - key: value.completed
        predicate: EQ
        value: true
  - type: task
    service: email
    taskKey: send
    inputs:
      to: __REPLACE_WITH_YOUR_EMAIL__
      subject: "Task completed"
      text: |
        Hello,

        A task has just been completed.
# liteflow.yml
workflows:
  create: ${ file('create.yml') } 
  fetch-todos: ${ file('fetch-todos.yml') }
  update: ${ file('update-todo.yml') }
  delete: ${ file('delete-todo.yml') }
  notify: ${ file('notify-todo.yml') }

You can now deploy this workflow and mark a to-do as done. After a few seconds, you should receive an email notifying you that a task has just been completed.

# Conclusion

In this tutorial, we have seen the following things:

  • How to store, query, update and delete data without having to set up any database
  • How to create APIs to access these data without having to create any server
  • How to connect our frontend to these APIs
  • How to run background jobs that react to the database to send email without setting up any email provider
Of course, this is a basic example of what you can do in the context of to-do list apps, but you can build anything you want using these features.

We would love to hear your feedback regarding this tutorial, and your experience with the creation of your to-do list app with Liteflow. Don't hesitate to share it with us in the comment section below.

# Bonus

Did you know that you can also deploy in one click your to-do list app on Liteflow in just a few seconds? Just click on the button below, sign up for free and visualize your app in the console 😉

New to Liteflow? Code and launch your applications quickly with a backend framework and integrated cloud solution. Visualize and manage applications anytime with the console. Sign up for free and deploy your idea in seconds!



play icon developers icon

Liteflow is built for developers

Discover Liteflow

Increase your product development speed

Grow your business faster with the Liteflow Toolkit for Lean Software Development

Sign up for free

No credit card required