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

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
Install Node.js: Ensure you have Node.js and npm (Node Package Manager) installed.
Download Node.jsCreate a Project Folder: Create a new folder for your project and navigate into it.
mkdir todo-api cd todo-apiInitialize 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
Create the main server file: Create a file named
index.js.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:
express: the Express.js frameworkbody-parser: a middleware that parses incoming request bodies in a format that's easy to work withcors: 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:
cors(): enables CORS to allow requests from different originsbodyParser.json(): parses incoming request bodies in JSON format
Defining the todos array
let todos = [];An empty array
todosis 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:
GET /todos: returns the entire
todosarray in JSON formatPOST /todos: adds a new todo item to the
todosarray. It expects ataskproperty in the request body. If thetaskproperty is present, it adds the todo item and returns a 201 Created response with the new todo item. If thetaskproperty is missing, it returns a 400 Bad Request response with an error message.DELETE /todos/:index: deletes a todo item by its index. It expects an
indexparameter in the URL. If the index is valid (i.e., within the bounds of thetodosarray), 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
PORTenvironment 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.jsFileconst 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}`); });
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_versionThe manifest version specifies the version of the manifest file format. In this case, it's version 3, which is the latest version.
nameandversionThese 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".
descriptionThis is a brief description of the extension.
permissionsThis specifies the permissions required by the extension. In this case, the extension requires access to the
storagepermission, which allows it to store and retrieve data locally.actionThis 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'spopup.html.default_icon: specifies the icon to be displayed in the browser toolbar. In this case, it's an image fileicons/images.pngwith a size of 16x16 pixels.
backgroundThis specifies the background script for the extension. In this case, it's a JavaScript file
background.jsthat 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 namedstyles.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 namedpopup.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
DOMContentLoadedevent, 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 IDtodo-formtodoInput: the HTML input element with the IDtodo-inputtodoList: the HTML element with the IDtodo-list, which will display the Todo List items
Fetch and Display Todos : The
fetchTodosfunction is defined to fetch the Todo List data from the API and display it in thetodoListelement. Here's what it does:It sends a GET request to the
API_URLusingfetch.It parses the response as JSON using
response.json().It clears the
todoListelement's inner HTML.It loops through the Todo List items and creates a new
<li>element for each item.It appends a delete button to each
<li>element using thecreateDeleteButtonfunction.It appends the
<li>element to thetodoListelement.
Create Delete Button : The
createDeleteButtonfunction creates a delete button for each Todo List item. Here's what it does:It creates a new
<button>element with the text "Delete".It adds an event listener to the button's
clickevent.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.It calls the
fetchTodosfunction to refresh the Todo List.
Add New Todo : The code adds an event listener to the
todoFormelement'ssubmitevent. When the form is submitted, it:Prevents the default form submission behavior using
event.preventDefault().Retrieves the user-inputted Todo List item from the
todoInputelement.Trims the input value to remove any whitespace.
If the input value is not empty, it sends a POST request to the API with the new Todo List item using
fetch.It clears the
todoInputelement's value.It calls the
fetchTodosfunction to refresh the Todo List.
Initial Fetch Finally, the code calls the
fetchTodosfunction 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
Open Chrome and go to
chrome://extensions/.Enable Developer Mode by toggling the switch on the top right.
Click on "Load unpacked" and select your
todo-extensionfolder.
Your extension should now be loaded and you can test it by clicking on the extension icon.

Testing the Full Integration
Start your Node.js API server if it's not already running.
node index.jsUse 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.






