Kurs
Düz bir yapılacaklar listesi başlangıçta gayet işe yarar: birkaç görev eklersiniz ve bitirdiğinizde kapatabilirsiniz. Ancak görevler artmaya devam ettikçe kullanımı zorlaşır. Bunu düzeltmenin bir yolu kategorilendirme eklemektir; böylece her görev İş, Çalışma, Kişisel vb. kategorilere ait olabilir ve listeyi ilgili kategoriye göre filtreleyebilirsiniz.
Bu eğitimde, Node.js ve MongoDB kullanarak görevleri kategoriye göre düzenlemenize olanak tanıyan bir REST API’nin nasıl oluşturulacağını öğreneceksiniz. Sonunda, oluşturma, listeleme, getirme, güncelleme ve silme olmak üzere beş uç noktanın yanı sıra bir kategori filtresine sahip çalışan bir Node.js sunucunuz olacak. Ayrıca, Mongoose gibi bir ODM’ye bağlı kalmadan yerel MongoDB sürücüsünü kullanarak kategori doğrulamasını nasıl zorunlu kılacağınızı da göreceksiniz.
Neler Öğreneceksiniz
- Node.js, Express ve MongoDB ile tam CRUD uç noktalarını içeren bir REST API’nin nasıl oluşturulacağı
- Girdilerin nasıl doğrulanacağı, kategorilerin nasıl zorunlu kılınacağı ve ObjectId değerlerinin güvenli biçimde nasıl işleneceği
- API’nizin nasıl yapılandırılıp test edileceği ve ardından bir ön yüze nasıl bağlanacağı
Bu eğitimin tüm kodunu GitHub’da bulabilirsiniz; isterseniz klonlayıp eş zamanlı olarak okuyabilirsiniz.
Önkoşullar
Başlamadan önce şunlara sahip olmalısınız:
- Node.js 18+ kurulu (npm varsayılan olarak dahildir)
- Yerelde çalışan MongoDB veya ücretsiz bir MongoDB Atlas kümesi
- Postman veya herhangi bir HTTP istemcisi
- Temel düzeyde JavaScript ve REST kavramları bilgisi
Adım 1: Projeyi Kurun
Basit görev yöneticimizi oluşturmaya başlayalım. İlk yapacağımız şey yeni bir klasör oluşturup terminalimizde aşağıdaki komutları çalıştırarak bir Node projesi başlatmak olacaktır:
mkdir task-manager-categories
cd task-manager-categories
npm init -y
Sırada, uygulamanın çalışması için gereken dört çalışma zamanı bağımlılığını kuracağız: yönlendirme için express, resmi MongoDB sürücüsü, ortam değişkenlerini yüklemek için dotenv ve son olarak güvenlik başlıkları için helmet.
npm install express mongodb dotenv helmet
Geliştirme için, sunucu dosyalar değiştiğinde otomatik olarak yeniden yüklensin diye aşağıdaki komutla nodemon’u geliştirici bağımlılığı olarak kurun:
npm install --save-dev nodemon
Sonrasında, proje klasörünü IDE’nizde açın ve package.json içindeki scripts bölümünü aşağıdakileri içerecek şekilde güncelleyin. Bu sayede sunucuyu npm start ile başlatabilir veya geliştirme modunda npm run dev ile çalıştırabilirsiniz:
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
İlerledikçe oluşturacağımız yapı aşağıdaki gibi olacak. Her klasörün belirli bir rolü var: db/ veritabanı bağlantısını yönetir, lib/ yardımcı işlevleri içerir, middleware/ Express ara yazılımları içindir ve routes/ istek işleyicilerini tanımlar. public/ klasörü ise daha sonra ekleyeceğimiz küçük bir ön yüzü barındıracak. Küçük bir projede bile bu yapı işleri takip etmeyi kolaylaştırır ve yeni kodun nereye eklenmesi gerektiğini netleştirir. İsterseniz klasör yapısını şimdi oluşturup ilerledikçe doldurun ya da bunu atlayıp adımlar boyunca her dosyayı sırasıyla ekleyin:
task-manager-categories/
├── db/
│ └── connect.js
├── lib/
│ └── taskDocument.js
├── middleware/
│ └── parseObjectId.js
├── routes/
│ └── tasks.js
├── public/
│ ├── index.html
│ ├── styles.css
│ └── app.js
├── .env
├── .env.example
├── .gitignore
├── package.json
└── server.js
Adım 2: MongoDB’ye Bağlanın
Görevleri depolamak ve almak için önce uygulamamızı MongoDB’ye bağlamamız gerekir. Bu, MongoDB sürücüsüne veritabanınıza nasıl bağlanacağını söyleyen bir bağlantı dizesi kullanılarak yapılır. Bunu doğrudan kodunuza gömmek yerine bir ortam dosyasında tutmak daha iyidir. Bu yaklaşım hassas değerleri kod tabanınızın dışında tutar ve ortamlar arasında geçişi kolaylaştırır.
Gerekli değişkenleri belgelemek için bir .env.example dosyası ve gerçek değerleriniz için bir .env dosyası oluşturun, şöyle:
# Copy this file to `.env` and fill in real values.
# --- Local MongoDB ---
# Use this if you're running MongoDB locally
MONGO_URI=mongodb://127.0.0.1:27017
# --- MongoDB Atlas ---
# Replace <username>, <password>, and <cluster-url> with your actual values
# Example: mongodb+srv://user:pass@cluster0.abcde.mongodb.net/
# MONGO_URI=mongodb+srv://<username>:<password>@<cluster-url>/?retryWrites=true&w=majority
DB_NAME=taskmanager
PORT=3000
Artık bir bağlantı dizesi hazır olduğuna göre, bunu MongoDB bağlantımızı kurmak için kullanabiliriz. Bir db/connect.js dosyası oluşturun. MongoDB istemcisini başlatacağımız ve uygulamanın geri kalanına erişilebilir kılacağımız yer burası olacak:
const { MongoClient } = require("mongodb");
let client;
let db;
async function connectDB() {
if (!process.env.MONGO_URI) {
throw new Error("MONGO_URI is not set. Check your .env file.");
}
client = new MongoClient(process.env.MONGO_URI, {
appName: "devrel-tutorial-javascript-crud-geeksforgeeks",
});
await client.connect();
db = client.db(process.env.DB_NAME || "taskmanager");
await db.collection("tasks").createIndex({ category: 1 });
console.log(`MongoDB connected (db: ${db.databaseName})`);
}
function getTasksCollection() {
if (!db) {
throw new Error("Database not initialized. Call connectDB() first.");
}
return db.collection("tasks");
}
async function closeDB() {
if (client) await client.close();
}
module.exports = { connectDB, getTasksCollection, closeDB };
Bu dosya, uygulamanın geri kalanının yeniden kullanabileceği tek bir MongoDB istemcisi kurar. Bunu yalnızca bir kez oluşturmak istersiniz; çünkü sürücü zaten arka planda bağlantı havuzlamayı yönetir. Her istek için yeni bir istemci oluşturmak ilk başta sorun değilmiş gibi görünse de kısa sürede performans sorunlarına yol açar.
Adım 3: Görev Yapısını ve Doğrulamayı Tanımlayın
Bu noktada uygulama MongoDB’ye bağlanabiliyor. Şimdi, herhangi bir şey kaydetmeye başlamadan önce bir görevin gerçekte nasıl göründüğünü tanımlamamız gerekiyor.
Yerel MongoDB sürücüsünü kullanmak burada biraz farklı hissettirmeye başlar. Mongoose’daki gibi bir şema dosyası yok. Bunun yerine “şema”, veritabanına eklediğiniz nesnenin biçimidir. İlk başta biraz gevşek gelebilir, ancak aslında faydalıdır; çünkü MongoDB’nin gerçekte ne yaptığına yakın kalırsınız ve hiçbir şey soyutlamaların arkasına saklanmaz.
Yine de doğrulama istiyoruz. Bu nedenle bu mantığı farklı rotalara dağıtmak yerine tek bir yerde tutacağız; böylece her şey aynı kurallara uyar. lib/taskDocument.js adlı bir dosya oluşturun ve aşağıdaki kodu ekleyin:
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
const ALLOWED_CATEGORIES = ['Work', 'Personal', 'Study', 'Other'];
function assertValidCategory(category) {
if (!ALLOWED_CATEGORIES.includes(category)) {
throw new ValidationError(
`category must be one of: ${ALLOWED_CATEGORIES.join(', ')}`
);
}
}
function buildTaskDocument(body = {}) {
if (!body.title || typeof body.title !== 'string' || !body.title.trim()) {
throw new ValidationError('title is required and must be a non-empty string');
}
if (body.category != null) {
assertValidCategory(body.category);
}
const now = new Date();
return {
title: body.title.trim(),
description: typeof body.description === 'string' ? body.description.trim() : '',
category: body.category != null ? body.category : 'Other',
completed: Boolean(body.completed),
createdAt: now,
updatedAt: now
};
}
function buildTaskUpdate(body = {}) {
const updates = {};
if (typeof body.title === 'string' && body.title.trim()) {
updates.title = body.title.trim();
}
if (typeof body.description === 'string') {
updates.description = body.description.trim();
}
if (body.category != null) {
assertValidCategory(body.category);
updates.category = body.category;
}
if (body.completed != null) {
updates.completed = Boolean(body.completed);
}
if (Object.keys(updates).length === 0) {
throw new ValidationError('no valid fields provided for update');
}
updates.updatedAt = new Date();
return updates;
}
module.exports = {
ALLOWED_CATEGORIES,
buildTaskDocument,
buildTaskUpdate,
ValidationError
};
Bu dosya, veritabanınıza girecek her şey için bekçilik yapar. Her oluşturma veya güncelleme isteği buradan geçer; böylece kuralları tek bir yerden uygularsınız.
Görevlerin daima doğru biçimde olmasını sağlar, kategorileri tutarlı kılar ve verilerinizi sorgulamaya başladığınızda sürprizleri önler. Özel ValidationError da hatalı girdileri gerçek sunucu sorunlarından temiz bir şekilde ayırmanıza olanak tanır; böylece API’niz uygun şekilde yanıt verebilir. Bu sayede uygulamanın geri kalanı basit kalır. Her rota, aldığı verinin zaten geçerli olduğunu bilerek kendi işine odaklanabilir. Sırada rotaları bağlayıp görevleri MongoDB’ye kaydetmeye başlamak var.
Adım 4: ObjectId ve İstek Doğrulamayı Yönetin
Artık bir görevin nasıl göründüğünü bildiğimize göre, bir göreve nasıl başvuracağımızı ele alalım. Bir rota bir :id parametresi içerdiğinde, bu değer düz bir metin olarak gelir. Ancak MongoDB bir ObjectId bekler. Bu dize yanlış biçimlendirilmişse, sürücü pek de yardımcı olmayan bir hata fırlatır. Bunu her rotada ele almak yerine, ara yazılımla merkezileştireceğiz; böylece her uç nokta aynı şekilde davranır.
Bunu yapmak için middleware/parseObjectId.js adlı bir dosya oluşturun ve aşağıdakileri ekleyin:
const { ObjectId } = require('mongodb');
function parseObjectId(req, res, next) {
const { id } = req.params;
if (!ObjectId.isValid(id)) {
return res.status(400).json({ error: 'invalid task id' });
}
req.taskId = new ObjectId(id);
next();
}
module.exports = parseObjectId;
Bu ara yazılım, rota işleyicinizden önce çalışır. id’nin geçerli olup olmadığını kontrol eder, onu bir ObjectId’ye dönüştürür ve req.taskId üzerine ekler. Böylece rota mantığınız çalıştığında her zaman doğru bir ObjectId ile uğraşırsınız ve hatalı girdiler net bir 400 yanıtıyla erken reddedilir. Sırada bunu rotalarımıza takıp her şeyi birbirine bağlamak var.
Adım 5: Görev Rotalarını Oluşturun (CRUD + Filtreleme)
Bu noktada işin büyük kısmı zaten tamamlandı. Geçerli bir görevin nasıl görüneceğini tanımladık ve kimliklerin nasıl ayrıştırılıp doğrulanacağını ele aldık. Bu da rota işleyicilerimizin tek bir şeye odaklanabilmesi anlamına geliyor: veritabanıyla konuşmak.
Gerçek API rotalarını oluşturmak için routes/tasks.js adlı bir dosya oluşturun ve aşağıdakileri ekleyin:
const express = require('express');
const { getTasksCollection } = require('../db/connect');
const {
ALLOWED_CATEGORIES,
buildTaskDocument,
buildTaskUpdate,
ValidationError
} = require('../lib/taskDocument');
const parseObjectId = require('../middleware/parseObjectId');
const router = express.Router();
// POST /tasks
router.post('/', async (req, res, next) => {
try {
const doc = buildTaskDocument(req.body);
const result = await getTasksCollection().insertOne(doc);
res.status(201).json({ _id: result.insertedId, ...doc });
} catch (err) {
if (err instanceof ValidationError) {
return res.status(400).json({ error: err.message });
}
next(err);
}
});
// GET /tasks (optionally ?category=Work)
router.get('/', async (req, res, next) => {
try {
const { category } = req.query;
if (category && !ALLOWED_CATEGORIES.includes(category)) {
return res.status(400).json({
error: `category must be one of: ${ALLOWED_CATEGORIES.join(', ')}`
});
}
const filter = category ? { category } : {};
const tasks = await getTasksCollection()
.find(filter)
.sort({ createdAt: -1 })
.toArray();
res.json(tasks);
} catch (err) { next(err); }
});
// GET /tasks/:id
router.get('/:id', parseObjectId, async (req, res, next) => {
try {
const task = await getTasksCollection().findOne({ _id: req.taskId });
if (!task) return res.status(404).json({ error: 'task not found' });
res.json(task);
} catch (err) { next(err); }
});
// PUT /tasks/:id
router.put('/:id', parseObjectId, async (req, res, next) => {
try {
const updates = buildTaskUpdate(req.body);
const result = await getTasksCollection().findOneAndUpdate(
{ _id: req.taskId },
{ $set: updates },
{ returnDocument: 'after' }
);
if (!result) return res.status(404).json({ error: 'task not found' });
res.json(result);
} catch (err) {
if (err instanceof ValidationError) {
return res.status(400).json({ error: err.message });
}
next(err);
}
});
// DELETE /tasks/:id
router.delete('/:id', parseObjectId, async (req, res, next) => {
try {
const result = await getTasksCollection().deleteOne({ _id: req.taskId });
if (result.deletedCount === 0) {
return res.status(404).json({ error: 'task not found' });
}
res.status(204).end();
} catch (err) { next(err); }
});
module.exports = router;
Yukarıdaki her rota aynı deseni izler: istekten girdi alınır, daha önce yazdığımız yardımcılar üzerinden geçirilir, MongoDB çağrılır ve ardından bir yanıt döndürülür. Doğrulama ve kimlik ayrıştırma önceden halledildiği için buradaki kod küçük ve öngörülebilir kalır. Pratikte şöyle işler:
- POST rotası, eklemeden önce buildTaskDocument ile yeni bir görev oluşturur
- GET rotaları isteğe bağlı olarak kategoriye göre filtreler ve sonuçları en yeni başta olacak şekilde döndürür
- PUT rotası buildTaskUpdate kullanır; böylece kısmi güncellemeler güvenli ve tutarlıdır
- DELETE rotası bir görevi önceden ayrıştırılmış ObjectId’sine göre siler
Bu koddaki bazı ayrıntılara da değinmeye değer:
- find() bir dizi değil, bir imleç döndürür; bu yüzden sonuçları gerçekten almak için sıralamadan sonra .toArray() zincirleriz
- returnDocument: 'after' ile findOneAndUpdate size güncellenmiş belgeyi anında verir
- Doğru HTTP durum kodlarını döndürüyoruz: oluşturma için 201, silme için 204 ve uygun yerlerde 400 veya 404
- Beklenmeyen hatalar next(err) ile iletilir; böylece her rotanın içinde tek tek ele almak yerine tek bir yerde işlenebilirler
Adım 6: Sunucuda Her Şeyi Birbirine Bağlayın
Bu noktada tüm parçalar yerli yerinde. Doğrulama, temiz rotalar ve çalışan bir veritabanı bağlantımız var. Şimdi her şeyi birbirine bağlayıp sunucuyu gerçekten başlatmamız gerekiyor. Bir server.js dosyası oluşturun. Uygulamanın giriş noktası burası; her şey burada bir araya geliyor:
require('dotenv').config();
const path = require('path');
const express = require('express');
const helmet = require('helmet');
const { connectDB, closeDB } = require('./db/connect');
const tasksRouter = require('./routes/tasks');
if (!process.env.MONGO_URI) {
console.error('Fatal: MONGO_URI is not set. Copy .env.example to .env and fill it in.');
process.exit(1);
}
const PORT = Number(process.env.PORT) || 3000;
const app = express();
app.use(helmet());
app.use(express.json({ limit: '100kb' }));
app.use(express.static(path.join(__dirname, 'public')));
app.get('/health', (req, res) => {
res.json({ status: 'ok', service: 'task-manager-with-categories' });
});
app.use('/tasks', tasksRouter);
app.use((req, res) => {
res.status(404).json({ error: 'not found' });
});
app.use((err, req, res, next) => {
if (err.type === 'entity.too.large') {
return res.status(413).json({ error: 'payload too large' });
}
if (err.type === 'entity.parse.failed') {
return res.status(400).json({ error: 'invalid JSON body' });
}
console.error(err);
res.status(500).json({ error: 'internal server error' });
});
async function start() {
try {
await connectDB();
const server = app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});
const shutdown = () => {
console.log('\nShutting down gracefully...');
server.close(async () => {
await closeDB();
process.exit(0);
});
setTimeout(() => process.exit(1), 10_000).unref();
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);
} catch (err) {
console.error('Failed to start server:', err);
process.exit(1);
}
}
if (require.main === module) {
start();
}
module.exports = { app, start };
Bu dosya her şeyi temiz bir şekilde birbirine bağlar. Her şey çalışmadan önce ortam değişkenlerinin ayarlı olduğunu kontrol eder, temel güvenliği helmet ile uygular ve uygulamanızın yanlışlıkla çok büyük yükleri kabul etmemesi için gelen JSON’u sınırlar. Ayrıca public/ klasörünüzü de sunar; böylece ön yüzünüz ayrı bir sunucuya ihtiyaç duymadan aynı uygulamada barınabilir.
Tüm rotalarınız /tasks altında bağlanır ve eşleşmeyen her şey temiz bir 404 döndürür. Hatalar her rotanın içinde tekrar edilmek yerine tek bir yerde ele alınır; bu da tutarlılığı ve bakımı kolaylaştırır. Veritabanı bağlantısı, sunucu dinlemeye başlamadan önce kurulur; böylece hazır olmadan istek kabul etmezsiniz ve kapatma işlemi zarifçe yapılır; bağlantılar düzgün şekilde kapatılır.
Şimdi npm run dev komutunu çalıştırarak her şeyin çalıştığından emin olalım. Her şey doğru şekilde bağlandıysa, aşağıdakine benzer bir çıktı görmelisiniz:
MongoDB connected (db: taskmanager)
Server running on http://localhost:3000
Bu noktada her şey bağlandı ve çalışıyor! Artık tamamen çalışan bir API’miz var; hadi test edelim.
Adım 7: API Uç Noktalarını Test Edin
Bir UI oluşturmadan önce, her uç noktanın tek başına çalıştığını doğrulamak değerlidir. Bu, hata ayıklamayı çok daha kolay hale getirir; çünkü bir sorunun API’den mi yoksa ön yüzden mi kaynaklandığını hızla anlayabilirsiniz. Postman’i (veya herhangi bir HTTP istemcisini) kullanacağız ve birkaç isteği sırayla çalıştıracağız. Her biri, bir önceki adımda oluşturulan veriler üzerine inşa edilir.
Bir görev oluşturun: Başlık, açıklama ve kategori içeren bir POST isteği gönderin. Her şey yolundaysa, API yeni görevi ve oluşturulan _id ile birlikte 201 Created döndürür.
POST http://localhost:3000/tasks
Content-Type: application/json
{
"title": "Write GeeksForGeeks article",
"description": "First draft by Friday",
"category": "Work"
}
Birkaç tane daha oluşturun: Aynı POST isteğini farklı gövdelerle çalıştırın; böylece listeleme ve filtrelemeyi test etmek için yeterli veriye sahip olursunuz. Bundan sonra sorgulayabileceğiniz küçük bir veri kümeniz olacak.
{ "title": "Go for a run", "category": "Personal" }
{ "title": "Read MongoDB docs", "category": "Study" }
{ "title": "Buy groceries" } // category defaults to "Other"
Tüm görevleri listeleyin: Bu, tüm görevleri en yeni başta olacak şekilde sıralı olarak döndürür.
GET http://localhost:3000/tasks
Kategoriye göre filtreleyin: Bu yalnızca İş (Work) görevlerini döndürür. Daha önce eklediğiniz indeks, verileriniz büyüdükçe bu sorguyu hızlı tutar.
GET http://localhost:3000/tasks?category=Work
Bir görevi güncelleyin: Az önce oluşturduğunuz görevlerden birinin _id değerini kullanın (POST yanıtından veya listeleme uç noktasından kopyalayabilirsiniz). Bu, güncellenmiş görevi döndürür. Yalnızca değiştirmek istediğiniz alanları göndermeniz yeterlidir; böylece kısmi güncellemeler basit kalır.
PUT http://localhost:3000/tasks/<paste-task-id-here>
Content-Type: application/json
{ "completed": true }
Bir görevi silin: Silmek istediğiniz görevin _id değerini kullanın. Bu, 204 No Content döndürür. Aynı görevi tekrar getirmeye çalışırsanız, "task not found" ile birlikte bir 404 alırsınız.
DELETE http://localhost:3000/tasks/<paste-task-id-here>
Bu noktada, tüm rotaların beklendiği gibi davrandığını doğruladınız. API işini yapıyor; şimdi üzerine bir kullanıcı arayüzü inşa edebiliriz.
Adım 8: API ile Etkileşim Kurmak İçin Bir Ön Yüz Ekleyin
Postman, API’yi doğrulamak için harikadır; ancak bunun gerçek bir web sitesinde nasıl çalıştığını görmek de güzel olur. Bu yüzden uygulamamıza biraz ön yüz kodu ekleyelim.
Buraya büyük HTML ve CSS dosyaları yapıştırmak yerine, kodu bu GitHub deposuna ekledim. Oraya gidin ve proje kökünüzdeki public/ klasörünüze index.html, styles.css ve favicon.svg içeriğini kopyalayın. Değişiklikleri kaydedin ve ardından localhost:3000 adresine gidin. Ardından aşağıdaki kodu app.js dosyasına yapıştırın:
const form = document.getElementById('task-form');
const titleEl = document.getElementById('title');
const descEl = document.getElementById('description');
const catEl = document.getElementById('category');
const errEl = document.getElementById('form-error');
const listEl = document.getElementById('task-list');
const emptyEl = document.getElementById('empty');
const filterEl = document.getElementById('filter');
const countEl = document.getElementById('task-count');
async function api(method, path, body) {
const res = await fetch(path, {
method,
headers: body ? { 'Content-Type': 'application/json' } : {},
body: body ? JSON.stringify(body) : undefined
});
if (res.status === 204) return null;
const data = await res.json();
if (!res.ok) throw new Error(data.error || `Request failed (${res.status})`);
return data;
}
const fetchTasks = (category) =>
api('GET', '/tasks' + (category ? `?category=${encodeURIComponent(category)}` : ''));
const createTask = (body) => api('POST', '/tasks', body);
const updateTask = (id, body) => api('PUT', `/tasks/${id}`, body);
const deleteTask = (id) => api('DELETE', `/tasks/${id}`);
function updateCount(tasks) {
const active = tasks.filter((t) => !t.completed).length;
const total = tasks.length;
if (total === 0) {
countEl.textContent = '';
return;
}
const scope = filterEl.value ? ` in ${filterEl.value}` : '';
countEl.textContent = `${total} tasks${scope} · ${active} active`;
}
function renderTask(task) {
const li = document.createElement('li');
li.className = 'task' + (task.completed ? ' completed' : '');
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.checked = task.completed;
checkbox.addEventListener('change', () =>
updateTask(task._id, { completed: checkbox.checked }).then(refresh)
);
const title = document.createElement('div');
title.textContent = task.title;
const badge = document.createElement('span');
badge.textContent = task.category;
const del = document.createElement('button');
del.textContent = 'Delete';
del.onclick = () => deleteTask(task._id).then(refresh);
li.append(checkbox, title, badge, del);
return li;
}
function render(tasks) {
listEl.innerHTML = '';
emptyEl.classList.toggle('hidden', tasks.length > 0);
for (const task of tasks) {
listEl.appendChild(renderTask(task));
}
updateCount(tasks);
}
async function refresh() {
try {
const tasks = await fetchTasks(filterEl.value);
render(tasks);
} catch (e) {
errEl.textContent = e.message;
}
}
form.addEventListener('submit', async (e) => {
e.preventDefault();
errEl.textContent = '';
const title = titleEl.value.trim();
if (!title) {
errEl.textContent = 'Title is required.';
return;
}
try {
await createTask({
title,
description: descEl.value.trim(),
category: catEl.value
});
form.reset();
await refresh();
} catch (e) {
errEl.textContent = e.message;
}
});
filterEl.addEventListener('change', refresh);
refresh();
JavaScript kodu, her şeyi daha önce oluşturduğunuz API’ye tekrar bağlayan yerdir. Her işlev rotalarınızdan birine karşılık gelir; bu da ön yüzü takip etmeyi kolaylaştırır. Tüm istekler tek bir api() yardımcısından geçer; böylece hataları her yerde tekrarlamak yerine yalnızca bir yerde ele alırsınız.
Filtreleme, arka uca zaten eklediğiniz mantığa doğrudan bağlanan ?category=Work gibi bir sorgu dizesi geçirerek çalışır. Bir görevi oluşturduktan, güncelledikten veya sildikten sonra uygulama en son listeyi yeniden çeker; böylece arayüz senkron kalır. Görev sayacı küçük bir detaydır ama uygulamayı kullanırken daha canlı hissettirir.
Şimdi sunucunuzu npm run dev ile yeniden başlatın ve ardından http://localhost:3000 adresini açarak Görev Yöneticisi’ni deneyin. Birkaç görev ekleyin, güncelleyin, kategoriler arasında geçiş yapın ve bazılarını da silin. Bu noktada ön yüz ve arka uç uçtan uca tamamen bağlı ve birlikte çalışıyor.
Özet
Tebrikler! Node.js, Express ve MongoDB kullanarak sıfırdan eksiksiz bir görev yöneticisi API’si oluşturdunuz. Doğrulamayı ele aldınız, rotalarınızı küçük tuttunuz ve her şeyi çalışan bir sisteme bağladınız. Daha da önemlisi, MongoDB’yi soyutlamaların arkasına saklamak yerine onun gerçekte nasıl çalıştığına yakın kaldınız. Buradan itibaren uygulamayı üretime hazır hale getirmek için geliştirmeye devam edebilirsiniz.
Önemli Noktalar
- Ara yazılım kullanmak ve doğrulamayı merkezileştirmek kodunuzu temiz ve öngörülebilir kılar.
- Yerel MongoDB sürücüsü, gereksiz soyutlamalar olmadan kontrol sağlar.
- İyi yapılandırılmış bir API, üretime hazır bir uygulamaya kolayca genişletilebilir.
SSS
MongoDB API’si Oluşturmak İçin Mongoose’a İhtiyacım Var mı?
Hayır. Bu eğitim, daha fazla kontrol sağlayan ve yapıyı hafif tutan yerel MongoDB sürücüsünü kullanır. Şema soyutlamaları istiyorsanız Mongoose’u daha sonra ekleyebilirsiniz.
Şema Olmadan Verileri Nasıl Doğrularım?
Doğrulamayı (örneğin buildTaskDocument gibi) yardımcı işlevlerde merkezileştirerek, veritabanına yazmadan önce her rotanın aynı kuralları uygulamasını sağlayabilirsiniz.
Geçersiz Bir ObjectId Gönderirsem Ne Olur?
Ara yazılım, bunu erken aşamada bir 400 yanıtıyla reddeder ve MongoDB’nin kafa karıştıran hatalar fırlatmasını engeller.
