Cara Restrukturisasi CRUD Node.js: Dari Function ke Folder Controller-Service-Model

Tutorial
Jul 18, 2026
15 mnt baca
441 tayangan
Cara Restrukturisasi CRUD Node.js: Dari Function ke Folder Controller-Service-Model
A

Admin

Penulis Artikel

Cara Restrukturisasi CRUD Node.js: Dari Function ke Folder Controller-Service-Model

Kalau kamu baru belajar backend dengan Node.js dan MongoDB, kemungkinan besar kode CRUD pertamamu terlihat seperti ini: semua fungsi create, read, update, delete ditulis di satu file, tanpa endpoint HTTP, tanpa struktur folder. Ini wajar — cara ini bagus untuk memahami logika dasar. Tapi begitu proyekmu mulai punya banyak fitur, kode seperti ini akan cepat berantakan dan sulit dikembangkan.

Artikel ini akan membahas cara merestrukturisasi kode CRUD dari kumpulan function menjadi struktur folder yang rapi menggunakan pola Controller-Service-Model, lengkap dengan contoh implementasi nyata: membangun Todo List API.

Artikel ini adalah bagian pertama dari seri “Backend Node.js untuk Pemula”. Setelah ini, folder yang kita bangun akan terus dikembangkan di artikel selanjutnya: Middleware dan .env di Express.js hingga Struktur Project Node.js Siap Produksi.


1. Sebelum Mulai: Apa Itu REST API, CRUD, dan HTTP Method?

Kalau istilah-istilah ini masih terdengar asing, tenang — bagian ini akan menjelaskannya dari nol sebelum kita masuk ke kode. Kalau kamu sudah familiar dengan REST API, CRUD, dan HTTP Method, boleh langsung lompat ke bagian 2.

1.1 Apa Itu API?

Analogi: bayangkan kamu makan di restoran. Kamu (pelanggan) tidak bisa langsung masuk ke dapur dan mengambil makanan sendiri — kamu memesan lewat pelayan, pelayan menyampaikan pesanan itu ke dapur, dapur memasak, lalu pelayan membawakan hasilnya kembali ke mejamu. Pelayan di sini berperan sebagai perantara yang menghubungkan dua pihak yang tidak berhubungan langsung.

Penjelasan teknis: API (Application Programming Interface) adalah “pelayan” itu, tapi dalam dunia software — perantara yang memungkinkan satu aplikasi (misalnya aplikasi mobile atau website) meminta data atau meminta suatu aksi dilakukan oleh aplikasi lain (dalam kasus kita, server backend yang kita bangun), tanpa aplikasi pertama perlu tahu persis bagaimana cara kerja di baliknya. Aplikasi frontend cukup tahu “cara memesan” (format request yang disepakati), tanpa perlu tahu bagaimana data itu sebenarnya disimpan atau diproses di server.

1.2 Apa Itu REST API?

Penjelasan teknis: REST (Representational State Transfer) adalah salah satu “gaya aturan” paling umum dipakai dalam membangun API berbasis web. REST API berkomunikasi lewat protokol HTTP — protokol yang sama dipakai browser untuk membuka halaman website — dan biasanya bertukar data dalam format JSON (format teks terstruktur yang mudah dibaca manusia maupun program, seperti yang sudah kamu lihat di contoh response pada bagian 5 nanti).

Ciri khas REST API yang akan terus kita pakai sepanjang seri ini:

  • Setiap data punya alamat (URL) sendiri, disebut endpoint — misalnya /api/todos untuk data todo.

  • Aksi apa yang ingin dilakukan terhadap data itu (mengambil, membuat, mengubah, menghapus) ditentukan lewat HTTP Method, dibahas di bagian 1.4.

  • Server tidak “mengingat” percakapan sebelumnya (disebut stateless) — setiap request harus membawa semua informasi yang dibutuhkan untuk diproses, termasuk nanti token autentikasi yang akan kita pelajari di artikel autentikasi JWT.

1.3 Apa Itu CRUD?

Analogi: anggap kamu punya buku catatan tugas sekolah. Ada empat hal dasar yang biasa kamu lakukan pada buku itu: menulis tugas baru di halaman kosong, membaca daftar tugas yang sudah ditulis, mencoret dan menulis ulang kalau ada tugas yang berubah, dan mencoret habis tugas yang sudah tidak relevan.

Penjelasan teknis: CRUD adalah singkatan dari empat operasi dasar yang hampir selalu dibutuhkan aplikasi apa pun saat berurusan dengan data:

  • Create — membuat data baru (menulis tugas baru).

  • Read — membaca/menampilkan data yang sudah ada (membaca daftar tugas).

  • Update — mengubah data yang sudah ada (merevisi tugas).

  • Delete — menghapus data (mencoret tugas).

Kalau kamu perhatikan lagi kode todo.js di bagian 2 nanti, keempat function di dalamnya (createTodo, getAllTodos/getTodoById, updateTodo, deleteTodo) sebenarnya adalah representasi langsung dari CRUD ini — hanya saja belum bisa diakses dari luar karena belum dibungkus jadi REST API.

1.4 HTTP Method: Menghubungkan CRUD ke REST API

Penjelasan teknis: di REST API, setiap operasi CRUD dipetakan ke salah satu HTTP Method berikut. Inilah “kosakata standar” yang dipakai pelayan (API) untuk tahu jenis pesanan apa yang diminta pelanggan (client):

Operasi CRUD

HTTP Method

Contoh Endpoint

Artinya

Create

POST

POST /api/todos

Membuat satu todo baru

Read

GET

GET /api/todos

Mengambil daftar semua todo

Read (satu data)

GET

GET /api/todos/:id

Mengambil satu todo spesifik berdasarkan ID

Update

PUT / PATCH

PUT /api/todos/:id

Mengubah data todo yang sudah ada

Delete

DELETE

DELETE /api/todos/:id

Menghapus todo

Kombinasi method + endpoint inilah yang membuat REST API mudah ditebak polanya. Begitu kamu tahu /api/todos adalah “alamat” data todo, kamu bisa langsung menebak bahwa DELETE /api/todos/123 pasti berarti “hapus todo dengan id 123”, tanpa perlu membaca dokumentasi lebih jauh. Pola tebak-mudah inilah yang akan terus kita jaga konsistensinya di setiap endpoint sepanjang seri ini — termasuk tabel referensi lengkap yang akan kamu lihat di bagian 5.

1.5 Kenapa Kode Function Biasa Belum Bisa Disebut REST API?

Sekarang kita sudah punya bekal untuk memahami masalah utama di bagian berikutnya: kode todo.js yang akan kita lihat sudah punya keempat operasi CRUD, tapi belum bisa diakses lewat HTTP method dan endpoint apa pun — artinya, secanggih apa pun logikanya, kode itu belum bisa disebut REST API sama sekali, karena tidak ada “pelayan” (lapisan HTTP) yang menjembataninya ke dunia luar. Itulah yang akan kita bangun di seluruh sisa artikel ini.


2. Persiapan Terminal untuk Pengguna Windows: Git Bash

Sepanjang artikel ini (dan seluruh seri), perintah terminal yang dicontohkan ditulis dalam sintaks Unix/Linux (bash) — misalnya mkdir -p untuk membuat beberapa folder sekaligus. Kalau kamu menjalankannya langsung di Command Prompt (cmd) atau PowerShell bawaan Windows, sebagian perintah ini tidak akan berjalan atau menghasilkan error, karena keduanya punya sintaks sendiri yang berbeda.

Analogi: anggap perintah bash yang dipakai di seri artikel ini itu seperti “bahasa nasional” yang dipahami hampir semua tutorial di internet, dokumentasi resmi Node.js, dan server Linux tempat aplikasi nanti di-deploy. Sementara itu, Command Prompt/PowerShell bawaan Windows itu seperti “bahasa daerah” yang hanya dipahami di lingkungan Windows saja. Git Bash berperan sebagai penerjemah — sebuah aplikasi terminal yang membuat komputer Windows-mu bisa “berbicara bahasa nasional” yang sama seperti yang dipakai di seri artikel ini, tanpa perlu mengganti sistem operasi.

Penjelasan teknis: khusus untuk perintah node dan npm (seperti npm init, npm install, npm run dev), sebenarnya tidak masalah dijalankan langsung di Command Prompt atau PowerShell, karena Node.js sendiri sudah cross-platform. Yang jadi masalah khusus adalah perintah pembuatan folder gaya Unix seperti mkdir -p src/config src/models src/services — flag -p dan kemampuan membuat banyak folder sekaligus dalam satu baris ini tidak dikenali sama persis oleh cmd maupun PowerShell bawaan Windows. Daripada harus mengetik ulang tiap perintah dengan sintaks Windows yang berbeda di setiap artikel, cara paling sederhana adalah memakai Git Bash untuk seluruh perintah terminal di seri ini, supaya kamu bisa langsung copy-paste persis seperti yang dicontohkan.

2.1 Instalasi Git Bash

Kalau kamu sudah menginstall Git sebelumnya (misalnya sudah pernah mengikuti artikel panduan push ke GitHub), Git Bash sudah otomatis terpasang di komputermu — keduanya berasal dari installer yang sama. Kalau belum, ikuti langkah berikut:

  1. Buka git-scm.com/download/win, download akan otomatis dimulai sesuai versi Windows-mu (64-bit/32-bit).

  2. Jalankan file installer yang sudah didownload.

  3. Klik Next di setiap langkah, biarkan seluruh opsi memakai pengaturan default (rekomendasi resmi dari Git untuk pemula) — termasuk opsi Adjusting your PATH environment, Choosing the default editor, dan Configuring the terminal emulator.

  4. Klik Install, tunggu proses selesai, lalu klik Finish.

Verifikasi instalasi berhasil: buka menu Start, cari dan buka aplikasi Git Bash, lalu ketik:

git --version
node --version
npm --version

Kalau ketiganya menampilkan nomor versi (bukan pesan error), berarti Git Bash sudah siap dipakai, dan sudah bisa mengakses instalasi Node.js yang sudah kamu install sebelumnya di Windows.

2.2 Menjadikan Git Bash sebagai Terminal Default di VS Code

Supaya tidak perlu bolak-balik membuka aplikasi Git Bash secara terpisah, kamu bisa menjadikannya terminal bawaan di dalam VS Code:

  1. Buka VS Code.

  2. Tekan Ctrl + Shift + P untuk membuka Command Palette.

  3. Ketik Terminal: Select Default Profile, lalu tekan Enter.

  4. Pilih Git Bash dari daftar pilihan yang muncul.

  5. Buka terminal baru dengan menekan Ctrl + ` (tombol backtick, biasanya satu tombol dengan tilde ~), atau lewat menu Terminal → New Terminal. Untuk tampilan Git Bash di vs code bisa cek contoh dibawah

gitbash

Kalau berhasil, prompt terminal di VS Code akan berubah tampilannya (biasanya diakhiri simbol $), menandakan kamu sekarang memakai Git Bash, bukan lagi PowerShell atau Command Prompt bawaan.

Mulai dari sini, setiap kali artikel ini (dan seri selanjutnya) menampilkan blok kode berlabel bash, jalankan perintah tersebut lewat Git Bash — baik lewat aplikasi Git Bash yang terbuka sendiri, maupun lewat terminal terintegrasi di VS Code yang sudah kamu atur di langkah 2.2. Perintah node dan npm akan tetap berjalan sama persis seperti biasa, karena keduanya memang sudah cross-platform sejak awal.


3. Kenapa Kode Function-Based Perlu Direstrukturisasi?

Bayangkan kode awal seperti ini (tanpa Express, tanpa endpoint — murni function untuk baca/tulis data):

// todo.js — versi awal, semua jadi satu
const todos = [];

function createTodo(title) {
  const todo = { id: Date.now(), title, completed: false };
  todos.push(todo);
  return todo;
}

function getAllTodos() {
  return todos;
}

function getTodoById(id) {
  return todos.find((t) => t.id === id);
}

function updateTodo(id, data) {
  const todo = getTodoById(id);
  if (!todo) return null;
  Object.assign(todo, data);
  return todo;
}

function deleteTodo(id) {
  const index = todos.findIndex((t) => t.id === id);
  if (index === -1) return false;
  todos.splice(index, 1);
  return true;
}

module.exports = { createTodo, getAllTodos, getTodoById, updateTodo, deleteTodo };

Kode ini berfungsi, tapi punya beberapa masalah begitu proyek berkembang:

  1. Tidak ada endpoint HTTP — belum bisa diakses lewat browser, Postman, atau frontend.

  2. Data disimpan di memori — hilang setiap server restart.

  3. Semua logika bercampur — jika nanti ditambah validasi, koneksi database, dan response HTTP, satu file ini akan membengkak jadi ratusan baris.

  4. Susah di-testing — logika bisnis (business logic) menyatu dengan logika penyimpanan data.

  5. Susah dikembangkan tim — kalau dua orang mengerjakan fitur berbeda di file yang sama, akan sering terjadi conflict.

Solusinya adalah memisahkan tanggung jawab (separation of concerns) ke dalam beberapa lapisan yang punya tugas jelas.


4. Konsep Controller-Service-Model

Pola ini membagi kode backend menjadi tiga lapisan utama:

Lapisan

Tugas

Contoh

Router

Menentukan endpoint dan method HTTP mana yang memanggil controller mana

POST /api/todostodoController.createTodo

Controller

Menangani request & response HTTP (menerima req, mengembalikan res)

Ambil req.body, panggil service, kirim res.json()

Service

Berisi business logic murni, tidak tahu soal HTTP

Validasi aturan bisnis, olah data sebelum/sesudah ke database

Model

Mendefinisikan struktur data dan cara berkomunikasi dengan database

Schema Mongoose untuk collection todos

Alur request-nya seperti ini:

Client (Postman/Browser)
   │
   ▼
Router  →  menentukan endpoint mana yang dipanggil
   │
   ▼
Controller  →  ambil data dari request, panggil service
   │
   ▼
Service  →  jalankan business logic, panggil model
   │
   ▼
Model  →  query ke MongoDB
   │
   ▼
Response dikirim balik ke Client

Keuntungan pola ini:

  • Mudah dilacak — kalau ada bug di query database, kamu tahu harus cek model. Kalau bug di validasi, cek service.

  • Reusable — service bisa dipanggil dari controller lain, atau dari script terpisah (misal seeding data), tanpa bergantung pada HTTP.

  • Mudah di-testing — service bisa ditest tanpa perlu menjalankan server HTTP.

  • Scalable — mudah ditambahkan lapisan baru seperti middleware, validasi, atau auth tanpa merusak struktur yang ada.


5. Implementasi: Refactor Todo List API

Sekarang kita praktikkan langsung. Kita akan membangun Todo List API dari nol dengan struktur folder yang benar, menggunakan Express dan MongoDB (Mongoose).

5.1 Setup Project

(Ingat: jalankan seluruh perintah berikut lewat Git Bash, bukan Command Prompt/PowerShell bawaan, sesuai penjelasan di bagian 2.)

mkdir todo-api
cd todo-api
npm init -y
npm install express mongoose
npm install --save-dev nodemon

Buka package.json, tambahkan script berikut di bagian "scripts":

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

5.2 Struktur Folder Final

Berikut struktur folder yang akan kita buat:

todo-api/
├── src/
│   ├── config/
│   │   └── db.js
│   ├── models/
│   │   └── todo.model.js
│   ├── services/
│   │   └── todo.service.js
│   ├── controllers/
│   │   └── todo.controller.js
│   ├── routes/
│   │   └── todo.routes.js
│   ├── app.js
│   └── server.js
├── package.json
└── node_modules/

Buat semua folder ini terlebih dahulu:

mkdir -p src/config src/models src/services src/controllers src/routes

5.3 Config: Koneksi Database

src/config/db.js

const mongoose = require("mongoose");

async function connectDB() {
  try {
    // URI database (dummy) masih ditulis langsung dulu di artikel ini.
    // Ganti placeholder kredensial, host cluster, dan nama database sesuai data koneksi di MongoDB Atlas Anda.
    const uri = "mongodb+srv://<username>:<password>@<cluster-name>.<cluster-id>.mongodb.net/<database-name>?retryWrites=true&w=majority&appName=<app-name>";

    await mongoose.connect(uri);
    console.log("MongoDB connected successfully");
  } catch (error) {
    console.error("MongoDB connection failed:", error.message);
    process.exit(1);
  }
}

module.exports = connectDB;

5.4 Model: Struktur Data Todo

src/models/todo.model.js

const mongoose = require("mongoose");

const todoSchema = new mongoose.Schema(
  {
    title: {
      type: String,
      required: true,
      trim: true,
    },
    description: {
      type: String,
      default: "",
    },
    completed: {
      type: Boolean,
      default: false,
    },
  },
  {
    timestamps: true, // otomatis menambahkan createdAt & updatedAt
  }
);

module.exports = mongoose.model("Todo", todoSchema);

5.5 Service: Business Logic

Service bertugas berkomunikasi dengan model dan menjalankan logika bisnis. Service tidak boleh tahu apa-apa soal req atau res — itu tugas controller.

src/services/todo.service.js

const Todo = require("../models/todo.model");

async function createTodo(data) {
  const todo = new Todo({
    title: data.title,
    description: data.description,
  });
  return await todo.save();
}

async function getAllTodos() {
  return await Todo.find().sort({ createdAt: -1 });
}

async function getTodoById(id) {
  return await Todo.findById(id);
}

async function updateTodo(id, data) {
  return await Todo.findByIdAndUpdate(
    id,
    {
      title: data.title,
      description: data.description,
      completed: data.completed,
    },
    { new: true, runValidators: true }
  );
}

async function deleteTodo(id) {
  return await Todo.findByIdAndDelete(id);
}

module.exports = {
  createTodo,
  getAllTodos,
  getTodoById,
  updateTodo,
  deleteTodo,
};

5.6 Controller: Jembatan HTTP ke Service

Controller bertugas menerima request, memanggil service, lalu mengirimkan response. Semua error ditangani di sini agar server tidak crash.

src/controllers/todo.controller.js

const todoService = require("../services/todo.service");

async function createTodo(req, res) {
  try {
    const { title, description } = req.body;

    if (!title) {
      return res.status(400).json({
        success: false,
        message: "Title is required",
      });
    }

    const todo = await todoService.createTodo({ title, description });

    return res.status(201).json({
      success: true,
      message: "Todo created successfully",
      data: todo,
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to create todo",
      error: error.message,
    });
  }
}

async function getAllTodos(req, res) {
  try {
    const todos = await todoService.getAllTodos();

    return res.status(200).json({
      success: true,
      message: "Todos retrieved successfully",
      data: todos,
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to retrieve todos",
      error: error.message,
    });
  }
}

async function getTodoById(req, res) {
  try {
    const { id } = req.params;
    const todo = await todoService.getTodoById(id);

    if (!todo) {
      return res.status(404).json({
        success: false,
        message: "Todo not found",
      });
    }

    return res.status(200).json({
      success: true,
      message: "Todo retrieved successfully",
      data: todo,
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to retrieve todo",
      error: error.message,
    });
  }
}

async function updateTodo(req, res) {
  try {
    const { id } = req.params;
    const { title, description, completed } = req.body;

    const updatedTodo = await todoService.updateTodo(id, {
      title,
      description,
      completed,
    });

    if (!updatedTodo) {
      return res.status(404).json({
        success: false,
        message: "Todo not found",
      });
    }

    return res.status(200).json({
      success: true,
      message: "Todo updated successfully",
      data: updatedTodo,
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to update todo",
      error: error.message,
    });
  }
}

async function deleteTodo(req, res) {
  try {
    const { id } = req.params;
    const deletedTodo = await todoService.deleteTodo(id);

    if (!deletedTodo) {
      return res.status(404).json({
        success: false,
        message: "Todo not found",
      });
    }

    return res.status(200).json({
      success: true,
      message: "Todo deleted successfully",
      data: deletedTodo,
    });
  } catch (error) {
    return res.status(500).json({
      success: false,
      message: "Failed to delete todo",
      error: error.message,
    });
  }
}

module.exports = {
  createTodo,
  getAllTodos,
  getTodoById,
  updateTodo,
  deleteTodo,
};

5.7 Router: Mendefinisikan Endpoint

src/routes/todo.routes.js

const express = require("express");
const router = express.Router();
const todoController = require("../controllers/todo.controller");

router.post("/", todoController.createTodo);
router.get("/", todoController.getAllTodos);
router.get("/:id", todoController.getTodoById);
router.put("/:id", todoController.updateTodo);
router.delete("/:id", todoController.deleteTodo);

module.exports = router;

5.8 App: Menyatukan Semua Komponen

src/app.js

const express = require("express");
const todoRoutes = require("./routes/todo.routes");

const app = express();

// Parsing JSON body dari request
app.use(express.json());

// Mendaftarkan semua route todo dengan prefix /api/todos
app.use("/api/todos", todoRoutes);

// Route dasar untuk cek server hidup
app.get("/", (req, res) => {
  res.json({ message: "Todo API is running" });
});

module.exports = app;

5.9 Server: Entry Point

src/server.js

const app = require("./app");
const connectDB = require("./config/db");

const PORT = 3000;

async function startServer() {
  await connectDB();

  app.listen(PORT, () => {
    console.log(`Server running on http://localhost:${PORT}`);
  });
}

startServer();

6. Menjalankan dan Menguji API

Pastikan kamu sudah mengganti placeholder uri di db.js dengan connection string MongoDB Atlas milikmu sendiri (username, password, nama cluster, dan nama database). Kalau kamu memilih menjalankan MongoDB secara lokal, ganti uri tersebut dengan mongodb://127.0.0.1:27017/todo_api_db.

Jalankan server:

npm run dev

Kalau berhasil, terminal akan menampilkan:

MongoDB connected successfully
Server running on http://localhost:3000
terminal berhasil konek ke database

Sekarang API sudah punya endpoint yang bisa dites lewat Postman atau curl:

Method

Endpoint

Fungsi

POST

/api/todos

Membuat todo baru

GET

/api/todos

Mengambil semua todo

GET

/api/todos/:id

Mengambil satu todo

PUT

/api/todos/:id

Mengupdate todo

DELETE

/api/todos/:id

Menghapus todo

Contoh test dengan curl untuk membuat todo baru:

curl -X POST http://localhost:3000/api/todos \
  -H "Content-Type: application/json" \
  -d '{"title": "Belajar struktur folder Express", "description": "Praktik controller-service-model"}'
hasil curl git bash

Response yang diharapkan:

{
  "success": true,
  "message": "Todo created successfully",
  "data": {
    "_id": "665f1c2e8b1e2a1a2c3d4e5f",
    "title": "Belajar struktur folder Express",
    "description": "Praktik controller-service-model",
    "completed": false,
    "createdAt": "2026-07-18T02:00:00.000Z",
    "updatedAt": "2026-07-18T02:00:00.000Z",
    "__v": 0
  }
}

7. Perbandingan Sebelum dan Sesudah

Aspek

Sebelum (Function-Based)

Sesudah (Controller-Service-Model)

Endpoint HTTP

Tidak ada

Ada, via Express Router

Penyimpanan data

Array di memori

MongoDB (Mongoose)

Pemisahan logic

Tidak ada, semua campur

Terpisah jelas per lapisan

Error handling

Tidak ada

Ditangani di controller dengan try-catch

Kemudahan dikembangkan

Sulit, satu file makin besar

Mudah, tinggal tambah file per fitur

Kemudahan testing

Sulit dipisah dari logic lain

Service bisa ditest terpisah


8. Kesimpulan

Merestrukturisasi kode CRUD dari kumpulan function menjadi pola Controller-Service-Model adalah langkah pertama menuju backend yang scalable dan mudah dirawat. Dengan memisahkan tanggung jawab tiap lapisan, kode menjadi lebih rapi, mudah ditelusuri saat debugging, dan lebih siap dikembangkan lebih lanjut — misalnya menambahkan middleware, autentikasi, atau validasi input.

Struktur folder Todo List API yang sudah kita bangun di artikel ini akan menjadi fondasi untuk artikel selanjutnya. Langkah berikutnya adalah membuat API ini lebih aman dan rapi menggunakan middleware dan environment variable.

Lanjutkan ke artikel berikutnya: Middleware dan .env di Express.js: Cara Bikin API Lebih Aman dan Rapi — di artikel ini, konfigurasi .env dan error handler global akan ditambahkan langsung ke project Todo List yang sudah kita buat.

Artikel Terkait

Best Practices Pengembangan Proyek Python dengan .env dan Dotenv

Pelajari cara mengelola konfigurasi proyek Python Anda dengan .env dan dotenv untuk pengembangan yang lebih efisien.

Baca Artikel
Cara Membuat Efek Parallax Mudah dengan parallaxx.js

Pelajari cara membuat efek parallax yang menakjubkan menggunakan parallax.js dalam tutorial ini.

Baca Artikel
Cara Menangani Error dan HTTP Status Code yang Benar

cara menentukan HTTP status code yang benar untuk tiap jenis error, dan cara membuat custom Error

Baca Artikel