Server Sync Series
Mutation (CRUD with Fetch API)
Bu blog yazısı Server Sync Seri yazılarımızdan bir tanesi. Burada CRUD(Creation, Read, Update ve Delete) içeren bir örneği FetchAPI üzerinden nasıl geliştirdiğimi anlatacağım.
Bu örnekteki amacımız CRUD işlemlerini içeren basit bir Task Management uygulaması geliştirmek. Bu geliştirmeleri yaparken;
- Network Katmanı (Fetch API)
- Mock Katmanı (Mock Server Worker)
- Data Katmanı (Mock Server Worker Data Rest)
- Task Uygulamasını
öğrenmiş olacağız. Bu uygulamanın kullanımı ile ilgili bilgilere aşağıdaki linklerden erişebilirsiniz.
Video: https://onurdayibasi.dev/videos-of-enterprise-react/NETWORK_MUTATION
Demo: https://onurdayibasi.dev/msw_data_rest
Source Codes: https://learnreactui.dev/contents/crud-mocking-msw
Uygulama aslında 2 temel unsurdan oluşuyor. TaskApp ve bunu karşılayan TaskHandler
Task Uygulamasının Geliştirimi
TaskApp uygulamasında öncelikle State tanımlamalarını yapıyoruz. Tüm task(görevleri) tutan state, yeni task girişini yapacağınız bir state ve son olarak güncellenme zamanı.
const [tasks, setTasks] = React.useState(null);
const [currentTitle, setCurrentTitle] = React.useState("");
const [lastUpdatedAt, setLastUpdatedAt] = React.useState(null);
Tüm Task(Görevlerin) listesini çekme
const fetchTasks = () => {
fetch(`${restApiUri}/tasks`)
.then((res) => res.json())
.then(setTasks)
.then(() => setLastUpdatedAt(Date.now()));
};
Bunu ilk sayfa yüklenirken 1 defa çekilmesini sağlıyoruz
React.useEffect(() => {
fetchTasks();
}, []);
Yeni bir Task Oluşturma fonksiyonu
const handleCreateTask = (event) => {
event.preventDefault();
fetch(`${restApiUri}/tasks`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
title: currentTitle,
}),
}).then(() => {
setCurrentTitle("");
fetchTasks();
});
};
Mevcut Task Güncelleme
const updateTask = (id, nextTask) => {
fetch(`${restApiUri}/tasks/${id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(nextTask),
}).then(fetchTasks);
};
Mevcut Task Silme
const deleteTask = (id) => {
fetch(`${restApiUri}/tasks/${id}`, { method: "DELETE" }).then(fetchTasks);
};
Aşağıda bahsedeceğim UI 3 parçadan oluşuyor;
- Yeni bir Task Girebileceğiniz Input Alanı
<header className={`relative ${hasTasks ? "shadow-sm" : ""}`}>
<form onSubmit={handleCreateTask}>
<Input
name="title"
value={currentTitle}
onChange={({ target }) => setCurrentTitle(target.value)}
autoComplete="off"
placeholder="Enter Task Name"
/>
</form>
</header>
- Görev (Task) Listesi
{hasTasks && (
<ul>
{tasks.map((task) => (
<li
key={task.id}
style={{
display: "flex",
justifyContent: "space-between",
padding: "0.5rem 1rem",
}}
>
<span>
<Checkbox
checked={task.isDone}
onChange={({ target }) => {
updateTask(task.id, {
title: task.title,
isDone: target.checked,
});
}}
/>
<span
style={{ cursor: "pointer", marginLeft: "1rem" }}
contentEditable={!task.isDone}
suppressContentEditableWarning={true}
onKeyPress={(event) => {
if (event.key === "Enter") {
event.preventDefault();
event.currentTarget.blur();
}
}}
onBlur={({ target }) => {
updateTask(task.id, {
title: target.textContent,
});
}}
>
{task.isDone ? <s>{task.title}</s> : task.title}
</span>
</span>
<Trash onClick={() => deleteTask(task.id)} />
</li>
))}
</ul>
Footer Alanı (Refetch Düğmesi ve LastUpdate Info Bilgilerini)
<footer>
<Button onClick={fetchTasks}>
<ArrowsClockwise />
Refetch
</Button>
<Typography.Paragraph>
Last synced{" "}
{new Date(lastUpdatedAt).toLocaleTimeString("en-US", {
seconds: "2-digit",
minutes: "2-digit",
hours: "2-digit",
day: "2-digit",
month: "long",
year: "numeric",
})}
</Typography.Paragraph>
</footer>
TaskHandler Uygulamasının Geliştirimi
Bu kısımda Faker, MSW ve MSWData kütüphanelerini kullanarak sanal bir sunucu oluşturmaya çalışacağız.
import { faker } from "@faker-js/faker";
import { factory, primaryKey } from "@mswjs/data";
import { HttpResponse, http } from "msw";
Öncelikle db de basit bir task tablosu oluşturuyoruz
export const db = factory({
tasks: {
id: primaryKey(faker.string.uuid),
title: String,
isDone: Boolean,
},
});
Tüm HTTP isteklerini GET, POST, PATCH, PUT, DELETE , MockServiceWorker sağlamış olduğu handler ile karşılayıp HTTPResponse ile verileri dönüyoruz.
export const taskHandlers = [
http.get(`${restApiUri}/tasks`, async () => {
return HttpResponse.json(db.tasks.getAll());
}),
http.get(`${restApiUri}/tasks/:id`, async ({ request }) => {
const { id } = request.params;
return HttpResponse.json(db.tasks.getById(id));
}),
http.post(`${restApiUri}/tasks`, async ({ request }) => {
const taskObj = await request.json();
const newEntity = db.tasks.create(taskObj);
return HttpResponse.json(newEntity);
}),
http.patch(`${restApiUri}/tasks`, async ({ request }) => {
const { idsToDelete } = await request.json();
const deletedTasks = db.tasks.deleteMany({
where: {
id: {
in: idsToDelete,
},
},
});
return HttpResponse.json(deletedTasks);
}),
http.put(`${restApiUri}/tasks/:id`, async ({ params, request }) => {
const { id } = params;
const taskObj = await request.json();
const updatedTask = db.tasks.update({
where: {
id: {
equals: id,
},
},
data: taskObj,
});
return HttpResponse.json(updatedTask);
}),
http.delete(`${restApiUri}/tasks/:id`, async ({ params }) => {
const { id } = params;
db.tasks.delete({
where: {
id: {
equals: id,
},
},
});
return new HttpResponse("Succeed", { status: 200, statusText: "OK" });
}),
];
Bundan sonraki kısımda mock verileri oluşturuyoruz
// Predefined static tasks with steps
const staticTasks = [
{
taskId: generateUUID(),
title: "Implement a new feature",
category: "Development",
isDone: false,
dueDate: getDueDate(),
steps: [
"Write code for the feature",
"Perform code review",
"Run tests to validate feature",
"Merge code into main branch",
],
},
....
Ve bu verileri sanal db(veritabanımıza kaydediyoruz)
// Function to insert predefined tasks into the database
for (let i = 0; i < staticTasks.length; i++) {
db.tasks.create(staticTasks[i]);
}
// Function to generate a static UUID (you can replace with your preferred UUID method)
function generateUUID() {
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
const r = (Math.random() * 16) | 0;
const v = c === "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
// Function to get a static due date (e.g., tomorrow, 3 days from now)
function getDueDate() {
const today = new Date();
const dueDate = new Date(today);
dueDate.setDate(today.getDate() + 3); // Set due date 3 days from now
return dueDate;
}
Okumaya Devam Et 😃
Bu yazının devamı veya yazı grubundaki diğer yazılara erişmek için bu linke tıklayabilirsiniz.