DEV Community

Cover image for Build a Todo App with Real APIs (No Backend Setup Required)
Crudify
Crudify

Posted on

Build a Todo App with Real APIs (No Backend Setup Required)

Tired of fake data and mock servers? Want to build real projects with real APIs?In this post, I'll show you how to build a simple yet powerful Todo App using Crudify.dev β€” a platform that provides real-world REST APIs without the backend hassle.


βœ… What We're Building

A clean, responsive Todo List App powered by Crudify.dev's Todo API. It supports:

  • βœ… Creating tasks
  • πŸ“ Editing tasks
  • βœ… Marking tasks complete/incomplete
  • ❌ Deleting tasks

And best of all β€” no backend code needed.


πŸ›  Tech Stack

  • HTML + CSS: For structure and style
  • JavaScript (Vanilla): For interacting with the API
  • *Crudify.dev:** For real, production-style REST APIs

πŸš€ Step-by-Step Breakdown

1. 🎨 UI Layout (HTML & CSS)

We started with a simple HTML structure:

<div class="container">
  <h1>Todo App</h1>
  <form id="todo-form">
    <input type="text" id="todo-input" placeholder="Enter a new task" required>
    <button type="submit">Add Task</button>
  </form>
  <ul id="todo-list"></ul>
</div>

Enter fullscreen mode Exit fullscreen mode

And styled it with responsive CSS that feels modern but minimal:

body {
    font-family: Arial, sans-serif;
    line-height: 1.6;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
}

.container {
    max-width: 500px;
    margin: 30px auto;
    padding: 20px;
    background-color: #fff;
    border-radius: 5px;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

h1 {
    text-align: center;
    color: #333;
}

#todo-form {
    display: flex;
    margin-bottom: 20px;
}

#todo-input {
    flex-grow: 1;
    padding: 10px;
    font-size: 16px;
    border: 1px solid #ddd;
    border-radius: 4px 0 0 4px;
}

button {
    padding: 10px 15px;
    background-color: #4caf50;
    color: #fff;
    border: none;
    cursor: pointer;
    font-size: 16px;
    border-radius: 0 4px 4px 0;
}

button:hover {
    background-color: #45a049;
}

#todo-list {
    list-style-type: none;
    padding: 0;
}

#todo-list li {
    background-color: #f9f9f9;
    padding: 10px;
    margin-bottom: 10px;
    border-radius: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.todo-actions button {
    margin-left: 5px;
    padding: 5px 10px;
    font-size: 14px;
    border-radius: 4px;
}

.todo-actions .edit {
    background-color: #2196f3;
}

.todo-actions .delete {
    background-color: #f44336;
}

.todo-actions .complete {
    background-color: #ff9800;
}

.completed {
    text-decoration: line-through;
    opacity: 0.6;
}
Enter fullscreen mode Exit fullscreen mode

2. 🧠 Using the Crudify.dev Todo API

Crudify gives you a real REST API β€” no backend required. Once logged in with GitHub, you get your API token, which you'll use like this:

const API_BASE_URL = "https://www.crudify.dev/api/v1/todo-lists";
const ACCESS_TOKEN = "your-api-token-here";

Enter fullscreen mode Exit fullscreen mode

Then use standard fetch calls to interact with your data.


3. πŸ”„ Fetching Todos

async function fetchTodos() {
  const response = await fetch(API_BASE_URL, {
    headers: { Authorization: `Bearer ${ACCESS_TOKEN}` }
  });
  const todos = await response.json();
  renderTodos(todos);
}

Enter fullscreen mode Exit fullscreen mode

This gives us a list of real tasks β€” just like a production app.


4. βž• Adding a Todo

todoForm.addEventListener("submit", async (e) => {
  e.preventDefault();
  const task = todoInput.value.trim();

  await fetch(API_BASE_URL, {
    method: "POST",
    headers: {
      Authorization: `Bearer ${ACCESS_TOKEN}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({ task }),
  });

  todoInput.value = "";
  fetchTodos();
});

Enter fullscreen mode Exit fullscreen mode

5. πŸ“ Edit, βœ… Complete, ❌ Delete

The app also supports editing, toggling completion, and deleting with just a few lines:

// Toggle completion
await fetch(`${API_BASE_URL}/${id}`, {
  method: "PATCH",
  headers: {
    Authorization: `Bearer ${ACCESS_TOKEN}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ isDone: !isDone }),
});

Enter fullscreen mode Exit fullscreen mode
// Delete a todo
await fetch(`${API_BASE_URL}/${id}`, {
  method: "DELETE",
  headers: { Authorization: `Bearer ${ACCESS_TOKEN}` },
});

Enter fullscreen mode Exit fullscreen mode

All actions automatically refresh the list, keeping your UI up to date.


🎁 Final Result

You get a fully working Todo App that:

  • Connects to a real API (with auth, status codes, errors)
  • Mimics real-world production apps
  • Requires zero backend setup
  • Works great with Crudify's design kits for Figma-to-code practice

πŸ“¦ Complete Code

Here's the full JavaScript that powers our app:

const API_BASE_URL = "https://www.crudify.dev/api/v1/todo-lists"
const ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."; // Your token will be different

const todoForm = document.getElementById("todo-form");
const todoInput = document.getElementById("todo-input");
const todoList = document.getElementById("todo-list");

// Fetch all todos
async function fetchTodos() {
    try {
        const response = await fetch(API_BASE_URL, {
            headers: {
                Authorization: `Bearer ${ACCESS_TOKEN}`,
            },
        });
        const todos = await response.json();
        renderTodos(todos);
    } catch (error) {
        console.error("Error fetching todos:", error);
        todoList.innerHTML = "<p>Failed to load todos.</p>";
    }
}

// Render todos
function renderTodos(todos) {
    todoList.innerHTML = "";
    todos.forEach((todo) => {
        const li = document.createElement("li");
        li.innerHTML = `
            <span class="${todo.isDone ? "completed" : ""}">${todo.task}</span>
            <div class="todo-actions">
                <button class="edit" data-id="${todo.id}">Edit</button>
                <button class="delete" data-id="${todo.id}">Delete</button>
                <button class="complete" data-id="${todo.id}" data-done="${todo.isDone}">
                    ${todo.isDone ? "Undo" : "Complete"}
                </button>
            </div>
        `;
        todoList.appendChild(li);
    });
}

// Add new todo
todoForm.addEventListener("submit", async (e) => {
    e.preventDefault();
    const task = todoInput.value.trim();
    if (!task) return;

    const submitButton = todoForm.querySelector("button");
    submitButton.textContent = "Adding...";
    submitButton.disabled = true;

    try {
        await fetch(API_BASE_URL, {
            method: "POST",
            headers: {
                Authorization: `Bearer ${ACCESS_TOKEN}`,
                "Content-Type": "application/json",
            },
            body: JSON.stringify({ task }),
        });
        todoInput.value = "";
        fetchTodos();
    } catch (error) {
        console.error("Error adding todo:", error);
    } finally {
        submitButton.textContent = "Add Task";
        submitButton.disabled = false;
    }
});

// Event delegation for edit, delete, and complete actions
todoList.addEventListener("click", async (e) => {
    const button = e.target;
    if (button.classList.contains("edit")) {
        const id = button.getAttribute("data-id");
        const newTask = prompt("Enter new task:");
        if (newTask) {
            await editTodo(id, newTask, button);
        }
    } else if (button.classList.contains("delete")) {
        const id = button.getAttribute("data-id");
        if (confirm("Are you sure you want to delete this task?")) {
            await deleteTodo(id, button);
        }
    } else if (button.classList.contains("complete")) {
        const id = button.getAttribute("data-id");
        const isDone = button.getAttribute("data-done") === "true";
        await toggleTodoCompletion(id, isDone, button);
    }
});

// Initial fetch of todos
fetchTodos();

Enter fullscreen mode Exit fullscreen mode

πŸ“¦ Code on GitHub

Want the full code?

πŸ‘‰ View Source on GitHub


πŸ’¬ Why Use Crudify.dev?

Most mock APIs don't let you:

  • Create/update/delete data
  • Practice auth headers
  • Handle 401/403/404 responses

Crudify.dev does all of that β€” and gives you real, job-ready experience.


πŸ”— Try It Yourself

  1. Visit Crudify.dev
  2. Log in with GitHub
  3. Grab your API token
  4. Clone the app or build your own

🧠 Final Thoughts

This tiny app demonstrates how easy it is to work with real APIs using Crudify.dev. Whether you're:

  • Practicing frontend dev
  • Building a portfolio
  • Preparing for interviews

β€” this tool is a game-changer.


πŸŽ‰ Big shoutout to Crudify.dev for making real-world development accessible for all frontend devs.


Top comments (0)