Skip to main content

Command Palette

Search for a command to run...

Easy Guide: Building a simple To-Do List API for Chrome Extensions

Updated
13 min read
Easy Guide: Building a simple To-Do List API for Chrome Extensions
J
IT Professional with 4+ years of combined experience across Software Engineering, DevOps, Cloud, Technical Writing, and AI-assisted Development. Passionate about building things, simplifying complex technology, and continuously learning while sharing knowledge through hands-on experimentation and technical writing.

We'll create a simple API that adds, deletes, and shows tasks in a to-do list using Node.js and Express. We'll also integrate it into a Chrome extension so we can interact with the API.

Here's a complete guide to creating a to-do list API, explaining the code and how it works.

Part 1: Setting Up the To-Do List API

We'll create a simple RESTful API using Node.js and Express.

Step 1: Initialize the Node.js Project

  1. Install Node.js: Ensure you have Node.js and npm (Node Package Manager) installed. Download Node.js

  2. Create a Project Folder: Create a new folder for your project and navigate into it.

     mkdir todo-api
     cd todo-api
    
  3. Initialize npm: Initialize your project with npm.

     npm init -y
    

Step 2: Install Dependencies

Install the necessary packages.

npm install express body-parser cors

Step 3: Create the API

  1. Create the main server file: Create a file named index.js.

  2. Start adding these code blocks:

    • Importing dependencies

        const express = require('express');
        const bodyParser = require('body-parser');
        const cors = require('cors');
      

      The code imports three dependencies:

      1. express: the Express.js framework

      2. body-parser: a middleware that parses incoming request bodies in a format that's easy to work with

      3. cors: a middleware that enables Cross-Origin Resource Sharing (CORS) to allow requests from different origins

    • Creating the Express app

        const app = express();
      

      An instance of the Express app is created.

    • Configuring middleware

        app.use(cors());
        app.use(bodyParser.json());
      

      Two middleware functions are added to the app:

      1. cors(): enables CORS to allow requests from different origins

      2. bodyParser.json(): parses incoming request bodies in JSON format

    • Defining the todos array

        let todos = [];
      

      An empty array todos is defined to store todo items.

    • Defining API endpoints

        // Get all todos
        app.get('/todos', (req, res) => {
          res.json(todos);
        });
      
        // Add a new todo
        app.post('/todos', (req, res) => {
          const { task } = req.body;
          if (task) {
            todos.push(task);
            res.status(201).json(task);
          } else {
            res.status(400).json({ error: 'Task is required' });
          }
        });
      
        // Delete a todo by index
        app.delete('/todos/:index', (req, res) => {
          const index = parseInt(req.params.index, 10);
          if (index >= 0 && index < todos.length) {
            const deletedTodo = todos.splice(index, 1);
            res.json(deletedTodo);
          } else {
            res.status(404).send('Todo not found');
          }
        });
      

      Three API endpoints are defined:

      1. GET /todos: returns the entire todos array in JSON format

      2. POST /todos: adds a new todo item to the todos array. It expects a task property in the request body. If the task property is present, it adds the todo item and returns a 201 Created response with the new todo item. If the task property is missing, it returns a 400 Bad Request response with an error message.

      3. DELETE /todos/:index: deletes a todo item by its index. It expects an index parameter in the URL. If the index is valid (i.e., within the bounds of the todos array), it removes the todo item at that index and returns the deleted todo item in JSON format. If the index is invalid, it returns a 404 Not Found response.

    • Starting the server

        const PORT = process.env.PORT || 3000;
        app.listen(PORT, () => {
          console.log(`Server is running on port http://localhost:${PORT}`);
        });
      

      The server is started on a port specified by the PORT environment variable or defaults to 3000. When the server is running, it logs a message to the console indicating the port it's listening on.

      index.js File

        const express = require('express');
        const bodyParser = require('body-parser');
        const cors = require('cors');
      
        const app = express();
      
        app.use(cors());
        app.use(bodyParser.json());
      
        let todos = [];
      
        // Get all todos
        app.get('/todos', (req, res) => {
            res.json(todos);
        });
      
        // Add a new todo
        app.post('/todos', (req, res) => {
            const { task } = req.body;
            if (task) {
                todos.push(task);
                res.status(201).json(task);
            } else {
                res.status(400).json({ error: 'Task is required' });
            }
        });
      
        // Delete a todo by index
        app.delete('/todos/:index', (req, res) => {
            const index = parseInt(req.params.index, 10);
            if (index >= 0 && index < todos.length) {
                const deletedTodo = todos.splice(index, 1);
                res.json(deletedTodo);
            } else {
                res.status(404).send('Todo not found');
            }
        });
      
        const PORT = process.env.PORT || 3000;
        app.listen(PORT, () => {
            console.log(`Server is running on port http://localhost:${PORT}`);
        });
      
  3. Run the Server: Start the server by running:

     node index.js
    

Step 4: Test the API

We will be testing this API in Postman. Download Postman

Basically we will be testing three endpoints,- GET /todos, POST /todos and DELETE /todos/:index

  • POST /todos - Let's add some tasks

  • GET /todos - Let's see what tasks we have to do

  • DELETE /todos/:index - Ok, Task done

    Test Passed!!!


Part 2: Creating the Chrome Extension

A Chrome extension consists of a manifest file, background script, and UI files (HTML, CSS, JS).

Step 1: Set Up the Project Structure

Create a new folder for your Chrome extension, e.g., todo-extension. Inside this folder, create the following structure:

todo-extension/
├── manifest.json
├── background.js
├── popup.html
├── popup.js
├── styles.css
├── icons

Step 2: Create the Manifest File

The manifest.json file tells Chrome about your extension.

{
    "manifest_version": 3,
    "name": "To-Do List Extension",
    "version": "1.0",
    "description": "A simple To-Do list Chrome extension.",
    "permissions": ["storage"],
    "action": {
        "default_popup": "popup.html",
        "default_icon": {
            "16": "icons/icon16.png",
        }
    },
    "background": {
        "service_worker": "background.js"
    }
}

Breakdown of the code:

  • manifest_version

    The manifest version specifies the version of the manifest file format. In this case, it's version 3, which is the latest version.

  • name and version

    These specify the name and version of the Chrome extension. In this case, the name is "To-Do List Extension" and the version is "1.0".

  • description

    This is a brief description of the extension.

  • permissions

    This specifies the permissions required by the extension. In this case, the extension requires access to the storage permission, which allows it to store and retrieve data locally.

  • action

    This specifies the default action for the extension. In this case, it has two properties:

    • default_popup: specifies the HTML file to be displayed when the user clicks the extension's icon. In this case, it's popup.html.

    • default_icon: specifies the icon to be displayed in the browser toolbar. In this case, it's an image file icons/images.png with a size of 16x16 pixels.

  • background

    This specifies the background script for the extension. In this case, it's a JavaScript file background.js that will run in the background whenever the extension is active.

Step 3: Create the Background Script

The background.js handles background tasks. In this simple example, we might not need it, but let's include a basic setup.

console.log('Background script running');

This line of code is written in JavaScript and is used to print a message to the console. The message being printed is 'Background script running'

In the context of a web browser, this code is being run as a background script, which is a type of script that runs in the background, separate from the web page. The purpose of this script is to perform tasks that do not require direct interaction with the web page. This can be useful for debugging purposes, as it allows the developer to verify that the script is running as expected.

Step 4: Create the Popup HTML

The popup.html is the UI of your extension.

<!DOCTYPE html>
<html>
<head>
    <title>To-Do List</title>
    <link rel="stylesheet" type="text/css" href="styles.css">
</head>
<body>
    <h1>To-Do List</h1>
    <form id="todo-form">
        <input type="text" id="todo-input" placeholder="New task">
        <button type="submit">Add</button>
    </form>
    <ul id="todo-list"></ul>
    <script src="popup.js"></script>
</body>
</html>

Breakdown of the code:

  • HTML Structure

      <!DOCTYPE html>
      <html>
        <head>
          ...
        </head>
        <body>
          ...
        </body>
      </html>
    

    The HTML document starts with the <!DOCTYPE html> declaration, followed by the <html> element, which contains the <head> and <body> elements.

  • Head Section

      <head>
        <title>To-Do List</title>
        <link rel="stylesheet" type="text/css" href="styles.css">
      </head>
    

    The <head> section contains metadata about the document. Here, we have:

    • <title>To-Do List</title>: sets the title of the page, which appears in the browser's title bar and in search engine results.

    • <link rel="stylesheet" type="text/css" href="styles.css">: links to an external stylesheet file named styles.css, which will be used to style the HTML elements.

  • Body Section

      <body>
        <h1>To-Do List</h1>
        <form id="todo-form">
          <input type="text" id="todo-input" placeholder="New task">
          <button type="submit">Add</button>
        </form>
        <ul id="todo-list"></ul>
        <script src="popup.js"></script>
      </body>
    

    The <body> section contains the content of the HTML document. Here, we have:

    • <h1>To-Do List</h1>: a heading element that displays the title "To-Do List".

    • <form id="todo-form">: a form element with an ID of "todo-form", which will be used to submit new tasks.

      • <input type="text" id="todo-input" placeholder="New task">: a text input field with an ID of "todo-input" and a placeholder text "New task".

      • <button type="submit">Add</button>: a submit button with the text "Add".

    • <ul id="todo-list"></ul>: an unordered list element with an ID of "todo-list", which will be used to display the to-do list items.

    • <script src="popup.js"></script>: a script element that links to an external JavaScript file named popup.js, which will be used to add functionality to the application.

Step 5: Create the Popup JavaScript

The popup.js file will handle the logic of interacting with the API.

document.addEventListener('DOMContentLoaded', () => {
    const API_URL = 'http://localhost:3000/todos';

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

    // Fetch and display todos
    const fetchTodos = async () => {
        const response = await fetch(API_URL);
        const todos = await response.json();
        todoList.innerHTML = '';
        todos.forEach((todo, index) => {
            const li = document.createElement('li');
            li.textContent = todo;
            li.appendChild(createDeleteButton(index));
            todoList.appendChild(li);
        });
    };

    // Create delete button
    const createDeleteButton = (index) => {
        const button = document.createElement('button');
        button.textContent = 'Delete';
        button.addEventListener('click', async () => {
            await fetch(`${API_URL}/${index}`, {
                method: 'DELETE',
            });
            fetchTodos();
        });
        return button;
    };

    // Add new todo
    todoForm.addEventListener('submit', async (event) => {
        event.preventDefault();
        const todo = todoInput.value.trim();
        if (todo) {
            await fetch(API_URL, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({ task: todo }),
            });
            todoInput.value = '';
            fetchTodos();
        }
    });

    fetchTodos();
});

Breakdown of the code:

  • Event Listener : The code starts by adding an event listener to the DOMContentLoaded event, which is fired when the initial HTML document has been completely loaded and parsed.

  • Variables and Elements : The code defines several variables and retrieves HTML elements using document.getElementById:

    • API_URL: the URL of the API that handles Todo List data (http://localhost:3000/todos)

    • todoForm: the HTML form element with the ID todo-form

    • todoInput: the HTML input element with the ID todo-input

    • todoList: the HTML element with the ID todo-list, which will display the Todo List items

  • Fetch and Display Todos : The fetchTodos function is defined to fetch the Todo List data from the API and display it in the todoList element. Here's what it does:

    1. It sends a GET request to the API_URL using fetch.

    2. It parses the response as JSON using response.json().

    3. It clears the todoList element's inner HTML.

    4. It loops through the Todo List items and creates a new <li> element for each item.

    5. It appends a delete button to each <li> element using the createDeleteButton function.

    6. It appends the <li> element to the todoList element.

  • Create Delete Button : The createDeleteButton function creates a delete button for each Todo List item. Here's what it does:

    1. It creates a new <button> element with the text "Delete".

    2. It adds an event listener to the button's click event.

    3. When the button is clicked, it sends a DELETE request to the API with the Todo List item's index as a parameter using fetch.

    4. It calls the fetchTodos function to refresh the Todo List.

  • Add New Todo : The code adds an event listener to the todoForm element's submit event. When the form is submitted, it:

    1. Prevents the default form submission behavior using event.preventDefault().

    2. Retrieves the user-inputted Todo List item from the todoInput element.

    3. Trims the input value to remove any whitespace.

    4. If the input value is not empty, it sends a POST request to the API with the new Todo List item using fetch.

    5. It clears the todoInput element's value.

    6. It calls the fetchTodos function to refresh the Todo List.

  • Initial Fetch Finally, the code calls the fetchTodos function to fetch and display the initial Todo List data.

Step 6: Create the Stylesheet

The styles.css file for basic styling.

Start adding these code blocks:

  • body styles

      body {
          font-family: Arial, sans-serif;
          width: 300px;
          padding: 10px;
      }
    

    This section sets the default styles for the <body> element, which is the root element of an HTML document.

    • font-family: Arial, sans-serif; sets the font family to Arial, with a fallback to a sans-serif font if Arial is not available.

    • width: 300px; sets the width of the body element to 300 pixels.

    • padding: 10px; adds a 10-pixel padding around the body element.

  • h1 styles

      h1 {
          font-size: 24px;
          margin-bottom: 10px;
      }
    

    This section sets the styles for <h1> headings.

    • font-size: 24px; sets the font size to 24 pixels.

    • margin-bottom: 10px; adds a 10-pixel margin below the heading.

  • form styles

      form {
          display: flex;
          margin-bottom: 10px;
      }
    

    This section sets the styles for <form> elements.

    • display: flex; sets the display property to flex, which allows the form elements to be laid out in a flexible box.

    • margin-bottom: 10px; adds a 10-pixel margin below the form.

  • input styles

      input {
          flex: 1;
          padding: 5px;
      }
    

    This section sets the styles for <input> elements.

    • flex: 1; sets the flex grow factor to 1, which means the input element will take up an equal amount of space in the flexible box.

    • padding: 5px; adds a 5-pixel padding around the input element.

  • button styles

      button {
          padding: 5px;
      }
    

    This section sets the styles for <button> elements.

    • padding: 5px; adds a 5-pixel padding around the button element.
  • ul styles

      ul {
          list-style-type: none;
          padding: 0;
      }
    

    This section sets the styles for <ul> (unordered list) elements.

    • list-style-type: none; removes the default list style (e.g., bullets or numbers).

    • padding: 0; removes any padding around the list.

  • li styles

      li {
          display: flex;
          justify-content: space-between;
          padding: 5px;
          border-bottom: 1px solid #ccc;
      }
    

    This section sets the styles for <li> (list item) elements.

    • display: flex; sets the display property to flex, which allows the list item elements to be laid out in a flexible box.

    • justify-content: space-between; sets the justify-content property to space-between, which distributes the elements evenly across the flexible box.

    • padding: 5px; adds a 5-pixel padding around the list item element.

    • border-bottom: 1px solid #ccc; adds a 1-pixel solid border at the bottom of the list item element, with a color of #ccc (a light gray).

styles.css File

body {
    font-family: Arial, sans-serif;
    width: 300px;
    padding: 10px;
}

h1 {
    font-size: 24px;
    margin-bottom: 10px;
}

form {
    display: flex;
    margin-bottom: 10px;
}

input {
    flex: 1;
    padding: 5px;
}

button {
    padding: 5px;
}

ul {
    list-style-type: none;
    padding: 0;
}

li {
    display: flex;
    justify-content: space-between;
    padding: 5px;
    border-bottom: 1px solid #ccc;
}

Part 3: Load the Chrome Extension

  1. Open Chrome and go to chrome://extensions/.

  2. Enable Developer Mode by toggling the switch on the top right.

  3. Click on "Load unpacked" and select your todo-extension folder.

Your extension should now be loaded and you can test it by clicking on the extension icon.


Testing the Full Integration

  1. Start your Node.js API server if it's not already running.

     node index.js
    
  2. Use the Chrome Extension: Open the extension popup, add, view, and delete tasks to see the full integration in action.

    Let's add tasks and try to follow along.

That's it! We've successfully created a To-Do list API and integrated it into a Chrome extension.

To make the API more effective:

some best practices for REST API design include using meaningful HTTP status codes, proper naming conventions, hypermedia for discoverability, and tools like Swagger or OpenAPI for documentation and client code generation.

To host the API :

use a cloud provider like AWS, Azure, or Google Cloud, use a PaaS provider like Heroku or DigitalOcean, or run it on a serverless platform like AWS Lambda or Azure Functions.

To secure the API :

use authentication and authorization mechanisms like OAuth 2.0 or JWT can be used. You can also use rate limiting and throttling to prevent abuse and ensure availability.

To monitor the API :

tools like New Relic or Datadog can be used to track performance and detect issues. You can also use logging and tracing to diagnose and troubleshoot problems.

To version the API :

semantic versioning can be used to indicate breaking changes and deprecate old versions, and API gateways and proxies can manage multiple versions and routes.

To automate the API development and deployment :

CI/CD pipelines and tools like Jenkins, Travis CI, or CircleCI, along with containerization and orchestration tools like Docker and Kubernetes, can be used to automate the API development and deployment.

To collaborate and share the API with other developers

API management platforms like Akana can be used.

More from this blog

D

Demystifying Tech with Jasai

104 posts

Demystifying Tech with Jasai is a blog dedicated to breaking down complex tech concepts into clear, beginner-friendly explanations. Covering DevOps, Docker, Git, AWS, CI/CD, Networking, and core programming fundamentals, it emphasizes strong foundations before advanced topics. Through step-by-step walkthroughs and real-world analogies, it simplifies the why behind the how — making technology approachable, structured, and built for long-term growth.