Lewati ke konten utama

Bangun Task Manager Sederhana dengan Kategori Menggunakan Node.js dan MongoDB

Pelajari cara mengelompokkan tugas berdasarkan kategori menggunakan Node.js dan MongoDB. Bangun REST API dengan endpoint create, list, get, update, delete serta filter kategori.
Diperbarui 10 Sep 2026  · 10 mnt baca

Jelajahi dengan AI

ChatGPTClaudePerplexity

Daftar tugas sederhana biasanya cukup di awal: Anda menambahkan beberapa tugas, lalu menutupnya setelah selesai. Namun saat jumlah tugas terus bertambah, penggunaannya menjadi tidak nyaman. Salah satu cara memperbaikinya adalah menambahkan kategorisasi, sehingga setiap tugas dapat masuk ke kategori seperti Work, Study, Personal, dan sebagainya, lalu Anda bisa memfilter daftar sesuai kategorinya.

Dalam tutorial ini, Anda akan belajar cara membangun REST API yang memungkinkan Anda mengelompokkan tugas berdasarkan kategori menggunakan Node.js dan MongoDB. Di akhir, Anda akan memiliki server Node.js yang berfungsi dengan lima endpoint: create, list, get, update, dan delete, beserta filter kategori. Anda juga akan melihat cara menerapkan validasi kategori menggunakan driver MongoDB native, tanpa bergantung pada ODM seperti Mongoose.

Apa yang Akan Anda Pelajari

  • Cara membangun REST API dengan Node.js, Express, dan MongoDB, termasuk endpoint CRUD lengkap
  • Cara memvalidasi input, menerapkan kategori, dan menangani nilai ObjectId dengan aman secara efektif
  • Cara menstrukturkan dan menguji API Anda lalu menghubungkannya ke frontend

Anda dapat menemukan kode lengkap untuk tutorial ini di GitHub jika Anda ingin mengkloningnya dan mengikuti sambil membaca.

Prasyarat

Sebelum mulai, Anda sebaiknya sudah memiliki:

  • Node.js 18+ terpasang (npm sudah termasuk secara default)
  • MongoDB berjalan secara lokal, atau cluster MongoDB Atlas gratis
  • Postman atau klien HTTP apa pun
  • Pengetahuan dasar JavaScript dan konsep REST

Langkah 1: Siapkan Proyek

Mari kita lanjut membangun task manager sederhana. Pertama, buat folder baru dan inisialisasi proyek Node dengan menjalankan perintah di bawah ini di terminal:

mkdir task-manager-categories
cd task-manager-categories
npm init -y

Berikutnya, kita akan memasang empat dependensi runtime yang dibutuhkan aplikasi untuk berjalan: express untuk routing, driver resmi MongoDB, dotenv untuk memuat variabel lingkungan, dan terakhir helmet untuk header keamanan.

npm install express mongodb dotenv helmet

Untuk pengembangan, pasang nodemon sebagai dev dependency dengan perintah di bawah agar server memuat ulang otomatis saat file berubah:

npm install --save-dev nodemon

Selanjutnya, buka folder proyek di IDE Anda dan perbarui bagian scripts di package.json untuk menyertakan hal berikut. Ini akan memungkinkan Anda memulai server dengan npm start atau menjalankannya dalam mode pengembangan dengan npm run dev:

"scripts": {
  "start": "node server.js",
  "dev": "nodemon server.js"
}

Berikut struktur yang akan kita bangun. Setiap folder punya peran jelas: db/ menangani koneksi database, lib/ berisi fungsi pembantu, middleware/ untuk middleware Express, dan routes/ mendefinisikan handler request. Folder public/ akan menampung frontend kecil yang akan kita tambahkan nanti. Bahkan untuk proyek kecil, struktur ini membuat alur mudah diikuti dan jelas ke mana kode baru harus ditempatkan. Silakan buat struktur folder sekarang dan isi sambil jalan, atau lewati ini dan tambahkan setiap file saat kita melangkah:

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

Langkah 2: Sambungkan ke MongoDB

Untuk menyimpan dan mengambil tugas, pertama kita perlu menghubungkan aplikasi ke MongoDB. Ini dilakukan menggunakan connection string, yang memberi tahu driver MongoDB cara terhubung ke database Anda. Alih-alih menuliskannya langsung di kode, sebaiknya simpan di file environment. Ini menjaga nilai sensitif tetap di luar codebase dan memudahkan berpindah antar lingkungan.

Buat file .env.example untuk mendokumentasikan variabel yang diperlukan, dan file .env untuk nilai aktual Anda, seperti ini:

# 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

Sekarang kita sudah punya connection string, kita bisa menggunakannya untuk menyiapkan koneksi MongoDB. Silakan buat file db/connect.js. Di sinilah kita menginisialisasi klien MongoDB dan membuatnya tersedia untuk bagian aplikasi lainnya:

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 };

File ini menyiapkan satu klien MongoDB yang dapat digunakan ulang oleh seluruh aplikasi. Anda hanya ingin membuat ini sekali, karena driver sudah menangani connection pooling di balik layar. Membuat klien baru pada setiap request mungkin terlihat baik di awal, tetapi akan cepat menimbulkan masalah performa.

Langkah 3: Definisikan Struktur dan Validasi Task

Pada tahap ini, aplikasi bisa terhubung ke MongoDB. Sekarang kita perlu mendefinisikan seperti apa bentuk sebuah task sebelum mulai menyimpan apa pun.

Di sinilah penggunaan driver MongoDB native terasa sedikit berbeda. Tidak ada file skema seperti saat menggunakan Mongoose. Sebaliknya, "skema" hanyalah bentuk objek yang Anda sisipkan ke database. Ini mungkin terasa agak longgar di awal, tetapi sebenarnya membantu karena Anda tetap dekat dengan cara MongoDB benar-benar bekerja, dan tidak ada yang disembunyikan oleh abstraksi.

Kita tetap menginginkan validasi. Jadi, alih-alih menyebarkan logika itu di berbagai route, kita akan menyimpannya di satu tempat agar semuanya mengikuti aturan yang sama. Silakan buat file bernama lib/taskDocument.js dan tambahkan kode berikut:

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
};

File ini bertindak sebagai gerbang untuk apa pun yang masuk ke database Anda. Setiap permintaan create atau update akan melewati sini, sehingga ada satu tempat yang menegakkan aturan.

Ini memastikan task selalu memiliki bentuk yang tepat, menjaga konsistensi kategori, dan menghindari kejutan saat Anda mulai melakukan query. ValidationError kustom juga memberi cara yang rapi untuk memisahkan input buruk dari masalah server sebenarnya, sehingga API Anda bisa merespons secara tepat. Dengan ini, bagian aplikasi lainnya bisa tetap sederhana. Setiap route bisa fokus pada tugasnya, dengan asumsi data yang diterima sudah valid. Selanjutnya, kita akan menyambungkan route dan mulai menyimpan task ke MongoDB.

Langkah 4: Tangani ObjectId dan Validasi Request

Sekarang kita sudah tahu seperti apa bentuk task, mari bahas cara mereferensikannya. Setiap kali sebuah route menyertakan parameter :id, nilai itu datang sebagai string biasa. Namun MongoDB mengharapkan sebuah ObjectId. Jika string itu tidak valid, driver akan melempar error yang kurang membantu. Alih-alih menangani itu di setiap route, kita akan memusatkannya dengan middleware agar setiap endpoint berperilaku sama.

Untuk melakukan ini, buat file bernama middleware/parseObjectId.js dan tambahkan berikut ini:

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;

Middleware ini berjalan sebelum handler route Anda. Ia memeriksa apakah id valid, mengonversinya menjadi ObjectId, dan menempelkannya ke req.taskId. Dengan begitu, saat logika route berjalan, Anda selalu bekerja dengan ObjectId yang benar, dan input buruk ditolak lebih awal dengan respons 400 yang jelas. Selanjutnya, kita akan memasangnya ke route dan mulai menyambungkan semuanya.

Langkah 5: Bangun Route Task (CRUD + Penyaringan)

Pada tahap ini, sebagian besar pekerjaan berat sudah selesai. Kita telah mendefinisikan seperti apa task yang valid, dan kita sudah menangani cara parsing dan validasi ID. Itu berarti handler route kita bisa tetap fokus pada satu hal: berbicara dengan database.

Mari kita bangun route API sebenarnya dengan membuat file bernama routes/tasks.js dan menambahkan berikut ini:

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;

Setiap route pada kode di atas mengikuti pola yang sama: mengambil input dari request, melewatkannya melalui helper yang kita buat sebelumnya, memanggil MongoDB, lalu mengembalikan respons. Dengan validasi dan parsing ID yang sudah ditangani, kode di sini tetap ringkas dan dapat diprediksi. Praktiknya seperti ini:

  • Route POST membangun task baru menggunakan buildTaskDocument sebelum menyisipkannya
  • Route GET secara opsional memfilter berdasarkan kategori dan mengembalikan hasil yang diurutkan dari yang terbaru
  • Route PUT menggunakan buildTaskUpdate, sehingga pembaruan parsial tetap aman dan konsisten
  • Route DELETE menghapus task berdasarkan ObjectId yang sudah diparse

Ada beberapa detail lain dalam kode ini yang juga patut dicatat:

  • find() mengembalikan cursor, bukan array, jadi kita rantai dengan .toArray() setelah sorting untuk benar-benar mendapatkan hasil
  • findOneAndUpdate dengan returnDocument: 'after' memberikan dokumen yang sudah diperbarui secara langsung
  • Kita mengembalikan kode status HTTP yang tepat: 201 untuk create, 204 untuk delete, dan 400 atau 404 sesuai kebutuhan
  • Error tak terduga diteruskan ke next(err), sehingga bisa ditangani di satu tempat alih-alih dalam setiap route

Langkah 6: Sambungkan Semuanya di Server

Pada titik ini, semua bagian sudah siap. Kita punya validasi, route yang bersih, dan koneksi database yang berfungsi. Sekarang kita hanya perlu menyambungkan semuanya dan benar-benar menjalankan server. Buat file server.js. Ini adalah entry point aplikasi, tempat semuanya dipadukan:

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 };

File ini merangkai semuanya dengan rapi. Ia memeriksa variabel lingkungan sebelum apa pun berjalan, menerapkan keamanan dasar dengan helmet, dan membatasi JSON yang masuk agar aplikasi Anda tidak menerima payload sangat besar secara tidak sengaja. Ia juga menyajikan folder public/, sehingga frontend Anda bisa berada dalam aplikasi yang sama tanpa memerlukan server terpisah.

Semua route Anda dipasang di bawah /tasks, dan apa pun yang tidak cocok akan mengembalikan 404 yang bersih. Error ditangani di satu tempat alih-alih diulang di setiap route, sehingga tetap konsisten dan mudah dirawat. Koneksi database dibuat sebelum server mulai mendengarkan, sehingga Anda tidak pernah menerima request sebelum siap, dan proses penghentian ditangani dengan baik, sehingga koneksi ditutup dengan benar.

Sekarang jalankan dan pastikan semuanya bekerja dengan menjalankan npm run dev. Jika semuanya tersambung dengan benar, Anda akan melihat seperti ini:

MongoDB connected (db: taskmanager)
Server running on http://localhost:3000

Pada titik ini, semuanya sudah tersambung dan berjalan! Kita kini memiliki API yang sepenuhnya berfungsi, jadi mari kita uji.

Langkah 7: Uji Endpoint API

Sebelum membangun UI, ada baiknya memastikan setiap endpoint berfungsi sendiri. Ini membuat debugging jauh lebih mudah, karena Anda dapat cepat mengetahui apakah masalah berasal dari API atau frontend. Kita akan menggunakan Postman (atau klien HTTP apa pun) dan menjalankan beberapa permintaan secara berurutan. Masing-masing bergantung pada data yang dibuat pada langkah sebelumnya.

Buat sebuah task: Kirim request POST dengan title, description, dan category. Jika berhasil, API mengembalikan 201 Created beserta task baru dan _id yang dihasilkan.

POST http://localhost:3000/tasks
Content-Type: application/json

{
  "title": "Write GeeksForGeeks article",
  "description": "First draft by Friday",
  "category": "Work"
}

Buat beberapa lagi: Jalankan request POST yang sama dengan body berbeda, sehingga Anda punya cukup data untuk menguji listing dan filtering. Setelah ini, Anda akan memiliki dataset kecil untuk di-query.

{ "title": "Go for a run", "category": "Personal" }
{ "title": "Read MongoDB docs", "category": "Study" }
{ "title": "Buy groceries" } // category defaults to "Other"

Daftar semua task: Ini mengembalikan semua task, diurutkan dari yang terbaru.

GET http://localhost:3000/tasks

Filter berdasarkan kategori: Ini hanya mengembalikan task kategori Work. Indeks yang Anda tambahkan sebelumnya menjaga query ini tetap cepat saat data bertambah.

GET http://localhost:3000/tasks?category=Work

Perbarui sebuah task: Gunakan _id dari salah satu task yang baru Anda buat (bisa disalin dari respons POST atau endpoint list). Ini mengembalikan task yang sudah diperbarui. Anda hanya perlu mengirim field yang ingin diubah, sehingga pembaruan parsial tetap sederhana.

PUT http://localhost:3000/tasks/<paste-task-id-here>
Content-Type: application/json

{ "completed": true }

Hapus sebuah task: Gunakan _id dari task yang ingin dihapus. Ini mengembalikan 204 No Content. Jika Anda mencoba mengambil task yang sama lagi, Anda akan mendapatkan 404 dengan "task not found".

DELETE http://localhost:3000/tasks/<paste-task-id-here>

Pada titik ini, Anda telah memastikan semua route berperilaku sesuai harapan. API sudah menjalankan tugasnya, jadi sekarang kita bisa lanjut membangun UI di atasnya.

Langkah 8: Tambahkan Frontend untuk Berinteraksi dengan API

Postman bagus untuk memverifikasi API, tetapi akan lebih baik melihat cara kerjanya di situs web nyata, jadi mari tambahkan sedikit kode frontend ke aplikasi kita.

Alih-alih menempelkan file HTML dan CSS besar di sini, saya sudah menambahkan kodenya ke repositori GitHub ini. Buka ke sana dan salin isi dari index.html, styles.css, dan favicon.svg ke folder public/ di root proyek Anda. Simpan perubahan lalu buka localhost:3000. Kemudian tempelkan kode di bawah ini ke file app.js:

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();

Kode JavaScript ini adalah tempat semuanya terhubung kembali ke API yang Anda bangun sebelumnya. Setiap fungsi dipetakan ke salah satu route Anda, sehingga frontend mudah diikuti. Semua request juga melalui satu helper api(), sehingga Anda hanya menangani error di satu tempat alih-alih mengulang logika yang sama di mana-mana.

Penyaringan bekerja dengan mengirim query string seperti ?category=Work, yang terhubung langsung ke logika backend yang sudah Anda tambahkan. Setelah membuat, memperbarui, atau menghapus task, aplikasi mengambil daftar terbaru lagi agar UI tetap sinkron. Penghitung task memang detail kecil, tetapi membuat aplikasi terasa lebih hidup saat digunakan.

Sekarang mulai ulang server Anda dengan npm run dev, lalu buka http://localhost:3000 untuk mencoba Task Manager. Tambahkan beberapa task, perbarui, beralih antar kategori, dan hapus juga. Pada tahap ini, frontend dan backend sudah sepenuhnya terhubung dan bekerja dari ujung ke ujung.

Ringkasan

Selamat! Anda berhasil membangun API task manager lengkap dari nol menggunakan Node.js, Express, dan MongoDB. Anda menangani validasi, menjaga route tetap ringkas, dan menyambungkan semuanya menjadi sistem yang berfungsi. Lebih penting lagi, Anda tetap dekat dengan cara MongoDB sebenarnya bekerja alih-alih menyembunyikannya di balik abstraksi. Dari sini, Anda bisa mulai mengembangkan aplikasi agar siap produksi.

Hal-Hal Penting

  • Menggunakan middleware dan memusatkan validasi menjaga kode Anda tetap bersih dan dapat diprediksi.
  • Driver MongoDB native memberi Anda kendali tanpa abstraksi yang tidak perlu.
  • API yang terstruktur dengan baik mudah diperluas menjadi aplikasi siap produksi.

FAQs

Apakah Saya Perlu Mongoose untuk Membangun API MongoDB?

Tidak. Tutorial ini menggunakan driver MongoDB native, yang memberi Anda lebih banyak kontrol dan tetap ringan. Anda dapat menambahkan Mongoose nanti jika menginginkan abstraksi skema.

Bagaimana Saya Memvalidasi Data Tanpa Skema?

Anda dapat memusatkan validasi dalam fungsi helper (seperti buildTaskDocument), memastikan setiap route menegakkan aturan yang sama sebelum menulis ke database.

Apa yang Terjadi Jika Saya Mengirim ObjectId Tidak Valid?

Middleware akan menolaknya lebih awal dengan respons 400, mencegah MongoDB melempar error yang membingungkan.

Topik
MongoDB

Kursus Teratas DataCamp

Kursus

Pengantar MongoDB di Python

3 Hr
24.2K
Kuasai manipulasi dan analisis data berstruktur fleksibel menggunakan MongoDB.
Lihat DetailRight Arrow
Mulai Kursus
Lihat Lebih BanyakRight Arrow
Terkait

blogs

40 Pertanyaan Wawancara DBMS Teratas di 2026

Kuasai pertanyaan wawancara basis data, dari konsep SQL dasar hingga skenario desain sistem tingkat lanjut. Panduan mendalam ini mencakup semua yang Anda perlukan untuk sukses di wawancara DBMS dan meraih peran berikutnya.
Dario Radečić's photo

Dario Radečić

15 mnt

blogs

12 Alternatif ChatGPT Terbaik yang Bisa Anda Coba pada 2026

Artikel ini menyajikan daftar alternatif ChatGPT yang akan meningkatkan produktivitas Anda.
Javier Canales Luna's photo

Javier Canales Luna

14 mnt

blogs

Tutorial Korelasi di R

Dapatkan pengenalan dasar-dasar korelasi di R: pelajari lebih lanjut tentang koefisien korelasi, matriks korelasi, plotting korelasi, dan sebagainya.
David Woods's photo

David Woods

13 mnt

blogs

Spaghetti Plot dan Jalur Badai

Temukan alasan mengapa Anda sebaiknya (tidak) menggunakan spaghetti plot untuk menyampaikan ketidakpastian jalur prediksi badai serta dampaknya terhadap interpretasi.
Hugo Bowne-Anderson's photo

Hugo Bowne-Anderson

13 mnt

Lihat Lebih BanyakLihat Lebih Banyak