Sitemap
Press enter or click to view image in full size

Next.js CRUD Frontend Integration(Using Express API)

--

Assume your backend is running at:

const BASE_URL = "http://localhost:5000/todos";

// or
// const BASE_URL = "https://todo-backend-rwmd.onrender.com/todos";

This will be used to call your API.

1. Fetch All Todos (GET)

This loads all todos when the page opens.

"use client";
import { useEffect, useState } from "react";

export default function page() {
const [todos, setTodos] = useState([]);
// Fetch data from backend
const fetchTodos = async () => {
const res = await fetch(BASE_URL);
const data = await res.json();
setTodos(data);
};
// Run once on page load
useEffect(() => {
fetchTodos();
}, []);
return (
<div>
<h1>Todos</h1>
{/* Display todos */}
{todos.map((todo) => (
<p key={todo._id}>{todo.title}</p>
))}
</div>
);
}

Explanation:

  • useState stores todos
  • useEffect runs when component loads
  • fetch() calls your backend API
  • .map() displays each todo

2. Create Todo (POST)

This adds a new todo to the database.

const addTodo = async () => {
await fetch("http://localhost:5000/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title: title}),
});

fetchTodos(); // refresh list after adding
};

Add this state:

 const [title, setTitle] = useState("");

Usage:

<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter todo"
/>

<button onClick={addTodo}>Add</button>

Explanation:

  • Sends data using POST
  • JSON.stringify converts object to JSON
  • Calls fetchTodos() to update UI

3. Update Todo (PUT)

This updates an existing todo.

const updateTodo = async (id) => {
if (!title) return;

await fetch(`${BASE_URL}/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: title,
completed: true,
}),
})
setEditingId(null);
setTitle("");
fetchTodos();
};

Get Shoomankhatri’s stories in your inbox

Join Medium for free to get updates from this writer.

Update EditingId on map function:

const [editingId, setEditingId] = useState(null);

<button
className="bg-yellow-400 py-4 px-6"
onClick={() => {
setEditingId(todo._id);
setTitle(todo.title);
}}
>
edit
</button>

Toggle Button

<div>
{editingId ? (
<button onClick={updateTodo}>Update</button>
) : (
<button onClick={addTodo}>Add</button>
)}
</div>

Usage:

<button onClick={() => updateTodo(todo._id)}>Update</button>

Explanation:

  • Uses PUT method
  • Sends updated fields
  • Uses id to target specific todo

4. Delete Todo (DELETE)

This removes a todo.

const deleteTodo = async (id) => {
await fetch(`${BASE_URL}/${id}`, {
method: "DELETE",
});
fetchTodos();
};

Usage:

<button onClick={() => deleteTodo(todo._id)}>Delete</button>

Explanation:

  • Uses DELETE request
  • Removes item by ID
  • Refreshes list after deletion

5. Combined Simple UI

A minimal working CRUD interface.

"use client";
import React, { useEffect, useState } from "react";
import { BASE_URL } from "../api/api";

const page = () => {
const [todos, setTodos] = useState([]);
const [title, setTitle] = useState("");
const [editingId, setEditingId] = useState(null);
// Fetch data from backend
const fetchTodos = async () => {
const res = await fetch(BASE_URL);
const data = await res.json();
setTodos(data);
};

const addTodo = async () => {
await fetch(BASE_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title }),
});
setTitle("");
fetchTodos(); // refresh list after adding
};

const updateTodo = async () => {
if (!title) return;

await fetch(`${BASE_URL}/${editingId}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({ title }),
});

setTitle(""); // clear input
setEditingId(null); // exit edit mode
await fetchTodos(); // refresh list
};

const deleteTodo = async (id) => {
await fetch(`${BASE_URL}/${id}`, {
method: "DELETE",
});
fetchTodos();
};

// Run once on page load
useEffect(() => {
fetchTodos();
}, []);
return (
<>
<div>todo list</div>

<input
value={title}
onChange={(e) => setTitle(e.target.value)}
placeholder="Enter todo"
/>
<div>
{editingId ? (
<button onClick={updateTodo}>Update</button>
) : (
<button onClick={addTodo}>Add</button>
)}
</div>

{todos.map((todo) => (
<p key={todo._id}>
{todo.title}

<button
className="bg-yellow-400 py-4 px-6"
onClick={() => {
setEditingId(todo._id);
setTitle(todo.title);
}}
>
edit
</button>


<button
className="bg-red-600 py-3s px-6"
onClick={() => deleteTodo(todo._id)}
>
Delete
</button>
</p>
))}

<div></div>
</>
);
};

export default page;

Explanation:

  • Input field stores new todo text
  • Add button sends POST request
  • Todos are listed dynamically
  • Delete button removes items

--

--