SR Modul Belajar

Modul Belajar

Membangun Aplikasi CRUD Armada dari Nol

Panduan langkah demi langkah untuk memasang, menjalankan, dan mempublikasikan aplikasi Sukarobot — sebuah CRUD sederhana berbasis React + Vite di sisi tampilan dan Express + MySQL di sisi data.

Level Pemula Durasi ± 45 menit Node 18+ Database MySQL
Mulai belajar

Alur data saat mode pengembangan.

01 — Pendahuluan

Apa yang akan kita bangun?

Sukarobot adalah aplikasi pengelolaan data armada robot. Satu halaman berisi daftar armada, kolom pencarian, formulir tambah/ubah, dan tombol hapus dengan konfirmasi. Semua data disimpan di MySQL melalui API.

Tambah dataFormulir dengan validasi kolom wajib.
Cari dataFilter nama, tipe, dan deskripsi secara langsung.
Ubah dataFormulir terisi otomatis dengan data yang dipilih.
Hapus dataKonfirmasi sebelum data benar-benar dihapus.

Teknologi yang dipakai

  • Backend: Node.js, Express 5, mysql2 — API JSON di port 5000.
  • Frontend: React 18 + Vite — halaman di port 5173.
  • Database: MySQL, database sukarobot, tabel armada.
  • Styling: satu file CSS biasa, tanpa framework.
Cara membaca modul

Ikuti urutannya dari atas ke bawah. Setiap bagian punya langkah bernomor dan perintah terminal yang bisa langsung disalin. Jangan lewati bagian Prasyarat dan Database — dua bagian itu paling sering jadi sumber error.

02 — Prasyarat

Yang perlu disiapkan

Ada tiga hal yang harus terpasang di komputer: Node.js, MySQL, dan terminal. Tidak perlu akun atau layanan berbayar untuk belajar di komputer sendiri.

Node.js 18 atau lebih baru

Unduh installer dari nodejs.org, pilih versi LTS, lalu install dengan opsi bawaan. Setelah selesai, buka terminal baru dan cek versinya:

terminal
node -v
npm -v

Angka pertama harus 18 atau lebih (contoh: v20.11.0). Kalau perintah tidak dikenali, tutup terminal lalu buka lagi agar PATH diperbarui.

MySQL

Pilih salah satu: MySQL Community Server, MariaDB, atau Laragon untuk pengguna Windows. Catat host, user, dan password yang kamu set saat instalasi — nanti dipakai di bagian Database.

Penting

MySQL harus sedang berjalan setiap kali backend dinyalakan. Kalau service MySQL mati, backend akan langsung berhenti dengan pesan gagal terhubung ke mysql.

Terminal / command prompt

Windows: Git Bash atau PowerShell. macOS/Linux: Terminal bawaan. Contoh perintah di modul ini memakai format bash; untuk PowerShell, cara mengatur environment variable ditulis di bagian Backend.

Editor kode

VS Code atau editor lain. Tidak wajib, tapi sangat membantu untuk membaca dan mengubah file.

03 — Struktur folder

Memahami isi project

Project terdiri dari dua aplikasi terpisah. Keduanya punya package.json sendiri, jadi npm install dijalankan dua kali — sekali di backend, sekali di frontend.

struktur project
sukarobot/
├─ backend/
│  ├─ db.js              koneksi + pembuatan database/tabel
│  ├─ server.js          route API Express
│  └─ package.json
├─ frontend/
│  ├─ index.html
│  ├─ vite.config.js     port 5173 + proxy /api
│  ├─ package.json
│  └─ src/
│     ├─ App.jsx         halaman utama
│     ├─ api.js          pemanggilan API
│     ├─ main.jsx        entry point React
│     ├─ styles.css
│     └─ components/
│        ├─ armadaList.jsx
│        └─ armadaForm.jsx
├─ index.php             modul belajar ini
└─ README.md

Siapa melakukan apa?

  • backend/server.js — menerima request HTTP, memvalidasi, lalu menjalankan query.
  • backend/db.js — membuat database dan tabel sekali saat start, lalu menyediakan fungsi query().
  • frontend/src/App.jsx — menyimpan state daftar armada, pencarian, dan form.
  • frontend/src/api.js — satu tempat untuk semua fetch ke /api/armada.
Catatan

Karena frontend memanggil /api (path relatif), semua route backend harus diawali /api. Kalau salah satu diubah, ubah keduanya: server.js dan src/api.js.

04 — Database

Menyiapkan database

Kabar baiknya: kamu tidak perlu membuat database secara manual. Saat backend pertama kali dijalankan, db.js otomatis membuat database sukarobot dan tabel armada bila belum ada.

  1. Pastikan MySQL menyala

    Lewat Laragon klik Start All, atau jalankan service MySQL di sistem operasi.

  2. Periksa kredensial di backend/db.js

    Default: host localhost, user root, password kosong, database sukarobot. Sesuaikan bila MySQL-mu memakai password.

  3. Jalankan backend sekali

    Perintahnya ada di bagian Backend. Kalau berhasil, database dan tabel langsung terbentuk.

Pengaturan koneksi

VariableDefaultKeterangan
DB_HOSTlocalhostLokasi server MySQL
DB_USERrootUser database
DB_PASSWORDkosongPassword user database
DB_NAMEsukarobotNama database
PORT5000Port server API

Nilai default bisa diganti tanpa mengedit file, cukup lewat environment variable sebelum perintah start:

terminal — pilih sesuai shell
# Git Bash, macOS, Linux
DB_PASSWORD=rahasia npm run dev

# PowerShell (Windows)
$env:DB_PASSWORD="rahasia"; npm run dev

# Command Prompt (Windows)
set DB_PASSWORD=rahasia && npm run dev

Skema tabel

Tabel armada memiliki lima kolom. Waktu dibuat otomatis oleh db.js:

KolomTipeKeterangan
idINT AUTO_INCREMENTPrimary key
namaVARCHAR(255)Nama armada, wajib
tipeVARCHAR(255)Tipe armada, wajib
deskripsiVARCHAR(255)Keterangan singkat, wajib
created_atTIMESTAMPTerisi otomatis saat data dibuat
updated_atTIMESTAMPDiperbarui otomatis saat data diubah
Kalau mengubah skema

Perintah CREATE TABLE IF NOT EXISTS tidak mengubah tabel yang sudah ada. Setelah mengedit struktur di db.js, hapus dulu tabel armada (atau jalankan ALTER TABLE manual), baru start ulang backend.

05 — Backend

Menjalankan server API

Backend adalah sumber data. Nyalakan lebih dulu sebelum frontend, karena halaman React langsung meminta daftar armada saat dibuka.

  1. Masuk ke folder backend
    terminal
    cd backend
  2. Pasang dependency

    Hanya perlu sekali, atau setiap kali package.json berubah.

    terminal
    npm install
  3. Jalankan server
    terminal
    npm run dev

    Jika berhasil muncul: api berjalan di http://localhost:5000.

Mode watch

npm run dev menjalankan node --watch server.js: setiap kali file backend disimpan, server restart otomatis. Untuk mode biasa tanpa watch, gunakan npm start.

Cek backend tanpa frontend

Buka terminal baru (biarkan server tetap berjalan), lalu uji dengan curl:

terminal — uji API
# Ambil semua armada (awalnya array kosong)
curl http://localhost:5000/api/armada

# Tambah satu armada
curl -X POST http://localhost:5000/api/armada \
  -H "Content-Type: application/json" \
  -d '{"nama":"Robot Alpha","tipe":"Otonom","deskripsi":"Robot pengantar barang"}'

Respons sukses berisi objek dengan id dan timestamp. Kalau muncul {"error":"..."}, baca pesannya — biasanya karena kolom kosong atau MySQL tidak siap.

Kalau port 5000 sudah dipakai

terminal
PORT=5001 npm run dev

Setelah itu sesuaikan juga proxy di frontend/vite.config.js ke http://localhost:5001, jika tidak frontend akan tetap mencari port 5000.

06 — Frontend

Menjalankan tampilan React

Frontend berjalan di port 5173 dan meneruskan semua request /api ke backend di port 5000. Inilah yang membuat frontend tidak perlu menulis alamat lengkap di setiap pemanggilan.

  1. Buka terminal baru

    Jangan tutup terminal backend. Modul ini butuh dua terminal berjalan bersamaan.

  2. Masuk ke folder frontend dan pasang dependency
    terminal
    cd frontend
    npm install
  3. Jalankan Vite
    terminal
    npm run dev

    Vite menampilkan alamat http://localhost:5173. Buka di browser.

Kenapa ada proxy?

Di vite.config.js terdapat pengaturan proxy: { '/api': 'http://localhost:5000' }. Artinya request ke http://localhost:5173/api/armada diteruskan ke backend tanpa mengubah path. Ini hanya berlaku saat npm run dev.

Perintah frontend lainnya

PerintahFungsi
npm run devServer pengembangan dengan hot reload
npm run buildMembuat versi produksi di folder dist/
npm run previewMelihat hasil build secara lokal
07 — Menjalankan aplikasi

Uji semua fitur

Dengan dua terminal aktif — backend di 5000 dan frontend di 5173 — buka http://localhost:5173 dan lakukan lima pemeriksaan berikut.

  • Halaman menampilkan judul Daftar Armada dan tombol Tambah Armada.
  • Klik Tambah Armada, isi ketiga kolom, lalu klik Simpan. Kartu baru muncul dan pesan sukses tampil.
  • Ketik kata kunci di kolom pencarian. Daftar menyempit sesuai nama, tipe, atau deskripsi.
  • Klik Ubah pada satu kartu, ganti namanya, lalu simpan. Perubahan langsung terlihat.
  • Klik Hapus, setujui konfirmasi, dan kartu tersebut hilang dari daftar.

Alur data satu kali simpan

  1. Form mengirim data ke api.create().
  2. api.js memanggil POST /api/armada.
  3. Vite meneruskan request ke Express di port 5000.
  4. server.js memvalidasi, menyimpan ke MySQL, dan mengembalikan baris baru.
  5. App.jsx menaikkan nilai versi sehingga daftar dimuat ulang.
Tanda backend belum siap

Kalau daftar kosong terus dan muncul pesan gagal memuat, buka http://localhost:5000/api/armada di browser. Jika alamat itu tidak merespons, masalahnya di backend atau MySQL — bukan di React.

08 — Endpoint API

Kontrak data

Semua endpoint berada di bawah /api/armada. Format request dan respons selalu JSON.

MethodPathFungsi
GET/api/armada?q=Daftar armada, opsional difilter kata kunci
GET/api/armada/:idAmbil satu armada
POST/api/armadaTambah armada
PUT/api/armada/:idUbah armada
DELETE/api/armada/:idHapus armada

Body request

POST / PUT — application/json
{
  "nama": "Robot Alpha",
  "tipe": "Otonom",
  "deskripsi": "Robot pengantar barang"
}

Contoh respons

200 OK
{
  "id": 1,
  "nama": "Robot Alpha",
  "tipe": "Otonom",
  "deskripsi": "Robot pengantar barang",
  "created_at": "2026-11-10 22:47:44",
  "updated_at": "2026-11-10 22:47:44"
}

Format error

StatusResponsPenyebab
400{"error":"Nama tidak boleh kosong"}Ada kolom wajib yang kosong
400{"error":"Data sudah ada"}Pelanggaran unique key (ER_DUP_ENTRY)
404{"error":"Data tidak ditemukan"}ID tidak ada di tabel
500{"error":"Terjadi kesalahan pada server"}Query gagal; detailnya dicetak di terminal backend

DELETE yang berhasil mengembalikan {"message":"Data berhasil dihapus"}.

09 — Bedah kode

Bagian penting yang perlu dipahami

Kamu tidak perlu menghafal semua baris. Fokus pada empat file ini: koneksi database, route API, pemanggil API di frontend, dan pengelolaan state halaman.

1. Koneksi database — backend/db.js

Koneksi memakai mysql2/promise agar bisa dipakai dengan async/await. Fungsi init() membuat database lebih dulu dengan koneksi tanpa nama database, baru setelah itu membuat pool dan tabel.

backend/db.js
const mysql = require('mysql2/promise');

const config = {
  host: process.env.DB_HOST || 'localhost',
  user: process.env.DB_USER || 'root',
  password: process.env.DB_PASSWORD || '',
  database: process.env.DB_NAME || 'sukarobot'
};

async function init() {
  const awal = await mysql.createConnection({
    host: config.host,
    user: config.user,
    password: config.password
  });
  await awal.query(`CREATE DATABASE IF NOT EXISTS \`${config.database}\``);
  await awal.end();

  pool = mysql.createPool({ ...config, dateStrings: true, connectionLimit: 10 });
  await pool.query(`CREATE TABLE IF NOT EXISTS armada (...)`);
}
Kenapa promise?

require('mysql2') memakai callback, sehingga await akan melempar error. Selalu gunakan require('mysql2/promise') untuk gaya async/await.

2. Route API — backend/server.js

Setiap route dibungkus try/catch dan memakai prepared statement (tanda ?) supaya nilai dari pengguna tidak disisipkan langsung ke SQL.

backend/server.js
app.get('/api/armada', async (req, res) => {
  try {
    const q = String(req.query.q ?? '').trim();
    const [rows] = await db.query(
      'SELECT * FROM armada WHERE nama LIKE ? OR tipe LIKE ? OR deskripsi LIKE ?',
      [`%${q}%`, `%${q}%`, `%${q}%`]
    );
    res.json(rows);
  } catch (err) {
    tanganiError(err, res);
  }
});

app.post('/api/armada', async (req, res) => {
  const { data, error } = validasi(req.body);
  if (error) return res.status(400).json({ error });

  const [hasil] = await db.query(
    'INSERT INTO armada (nama, tipe, deskripsi) VALUES (?, ?, ?)',
    [data.nama, data.tipe, data.deskripsi]
  );
  res.status(201).json(await ambilSatu(hasil.insertId));
});

3. Pemanggil API — frontend/src/api.js

Semua fetch dikumpulkan di satu objek. Fungsi request() menangani header JSON, membaca respons, dan melempar error bila status bukan 2xx.

frontend/src/api.js
const BASE = '/api/armada';

async function request(url, options = {}) {
  const res = await fetch(url, {
    headers: { 'Content-Type': 'application/json' },
    ...options,
  });
  const body = res.status === 204 ? null : await res.json();
  if (!res.ok) throw new Error(body?.error || 'Terjadi kesalahan.');
  return body;
}

export const api = {
  list: (q = '') => request(`${BASE}?q=${encodeURIComponent(q)}`),
  create: (data) => request(BASE, { method: 'POST', body: JSON.stringify(data) }),
  update: (id, data) => request(`${BASE}/${id}`, { method: 'PUT', body: JSON.stringify(data) }),
  remove: (id) => request(`${BASE}/${id}`, { method: 'DELETE' }),
};

4. State halaman — frontend/src/App.jsx

Daftar dimuat ulang setiap kali q (kata pencarian) atau versi (penanda data berubah) ikut berubah. Setelah simpan atau hapus, cukup naikkan versi — tidak perlu mengubah array secara manual.

frontend/src/App.jsx (ringkas)
const [armadas, setArmadas] = useState([]);
const [q, setQ] = useState('');
const [versi, setVersi] = useState(0);

useEffect(() => {
  let aktif = true;

  async function muat() {
    const data = await api.list(q);
    if (aktif) setArmadas(data);
  }

  muat();
  return () => { aktif = false; };
}, [q, versi]);

// setelah simpan / hapus:
setVersi((v) => v + 1);

Penanda aktif mencegah respons lama menimpa hasil pencarian yang lebih baru saat pengguna mengetik cepat.

10 — Build & publikasi

Menaikkan ke domain sendiri

Ada dua hal berbeda yang dipublikasikan: halaman modul ini (index.php) dan aplikasinya (frontend + backend + MySQL). Keduanya bisa berada di domain yang sama, tetapi cara menyiapkannya berbeda.

Langkah 1 — build frontend

terminal
cd frontend
npm run build

Perintah ini menghasilkan folder frontend/dist berisi index.html dan aset statis. Folder itulah yang diunggah ke hosting.

Langkah 2 — sesuaikan alamat API

Saat mode pengembangan, /api diteruskan oleh Vite. Di produksi tidak ada Vite, jadi ada dua pilihan:

  • Satu domain dengan reverse proxy. Arahkan /api/* ke server Node, misalnya lewat Nginx. Frontend tetap memakai BASE = '/api/armada'.
  • Domain API terpisah. Ubah BASE di src/api.js menjadi alamat lengkap, contoh https://api.domainmu.com/api/armada, lalu build ulang. Backend sudah mengaktifkan CORS, sehingga request lintas domain diizinkan.
frontend/src/api.js
// opsi 2: backend di subdomain terpisah
const BASE = 'https://api.domainmu.com/api/armada';

Langkah 3 — siapkan hosting backend

Backend butuh runtime Node.js dan koneksi ke MySQL. Ini tidak bisa dijalankan di hosting PHP biasa. Pilihan yang umum:

Jenis hostingCocok untukCatatan
VPS (Ubuntu)Belajar deployment penuhInstall Node, MySQL, Nginx, lalu jalankan dengan PM2
Platform Node (Railway, Render, dsb.)Cepat onlineSet env DB_* di dashboard; sediakan MySQL terkelola
cPanel dengan fitur Node.jsHosting yang mendukungCek dulu tersedia “Setup Node.js App” dan MySQL remote
Penting soal hosting PHP

index.php hanya untuk halaman dokumentasi. Hosting yang hanya mendukung PHP tidak bisa menjalankan server.js atau MySQL aplikasi. Frontend boleh diunggah sebagai file statis, tetapi API tetap harus berjalan di server Node.

Langkah 4 — unggah dokumentasi

Unggah index.php ke folder publik domain (misalnya public_html/modul/index.php). File ini mandiri — tidak butuh database, build, atau file lain. Buka https://domainmu.com/modul/ dan modul siap dibaca.

Checklist produksi

  • Password MySQL tidak lagi kosong; simpan lewat environment variable di server.
  • BASE di src/api.js sudah menunjuk alamat API produksi (atau reverse proxy dikonfigurasi).
  • Frontend sudah di-build ulang setelah mengubah BASE.
  • Backend berjalan permanen (PM2, systemd, atau proses platform), bukan hanya npm run dev.
  • Coba semua endpoint dari browser/Postman setelah online.
11 — Troubleshooting

Error yang sering muncul

Sebagian besar masalah berasal dari tiga hal: MySQL belum siap, backend belum jalan, atau alamat API tidak cocok. Buka bagian yang sesuai.

Backend berhenti dengan “gagal terhubung ke mysql”

MySQL tidak menerima koneksi. Nyalakan service MySQL (Laragon: Start All), lalu jalankan ulang backend. Pastikan host di db.js benar.

“Access denied for user 'root'@'localhost'”

Password di konfigurasi tidak cocok. Set DB_PASSWORD sesuai password MySQL-mu, atau kosongkan bila memang tanpa password. Untuk PowerShell: $env:DB_PASSWORD="rahasia"; npm run dev.

“EADDRINUSE: address already in use :::5000”

Port 5000 dipakai proses lain. Hentikan proses tersebut, atau jalankan PORT=5001 npm run dev lalu sesuaikan proxy di vite.config.js.

Frontend menampilkan “Gagal memuat data armada”

Buka http://localhost:5000/api/armada langsung di browser. Kalau gagal, backend bermasalah. Kalau berhasil, periksa proxy di vite.config.js dan pastikan BASE di src/api.js adalah /api/armada.

Halaman blank / layar putih

Buka konsol browser (F12). Error biasanya berupa import file yang tidak ada atau typo pada nama komponen. Pastikan semua file di src/ benar-benar ada, lalu simpan ulang.

“Cannot find module ...” saat npm run dev

Dependency belum terpasang atau terminal berada di folder yang salah. Jalankan npm install di folder yang memiliki package.json sesuai aplikasi yang dijalankan.

Data tidak muncul setelah menambah kolom di db.js

CREATE TABLE IF NOT EXISTS tidak mengubah tabel lama. Hapus tabel armada lewat phpMyAdmin, atau jalankan ALTER TABLE armada ADD COLUMN ..., lalu restart backend.

API berhasil di localhost tetapi gagal setelah online

Biasanya BASE masih relatif (/api/armada) padahal backend ada di domain lain, atau server Node tidak berjalan. Periksa tab Network di DevTools: lihat URL yang dipanggil dan status responsnya.

12 — Latihan

Setelah semuanya berjalan

Cara terbaik memastikan paham adalah membangun ulang. Mulai dari project kosong, lalu bandingkan hasilnya dengan kode ini.

  1. Bangun ulang dari nol

    Buat folder backend dan frontend, lalu tulis ulang file satu per satu tanpa menyalin. Fokus pada alur request, bukan pada hafalan.

  2. Tambah satu kolom baru

    Misalnya status dengan nilai Aktif atau Perbaikan. Ubah tabel, validasi, route, form, dan tampilan kartu. Ini melatih pemahaman dari database sampai UI.

  3. Tambahkan pagination

    Tambahkan ?page= dan ?limit= di backend, lalu tombol “Sebelumnya / Berikutnya” di frontend.

  4. Rapikan pesan error

    Buat pemberitahuan yang lebih ramah untuk jaringan putus, alih-alih hanya teks error dari API.

  5. Publikasikan

    Ikuti bagian Build & publikasi. Menghadapi error deployment adalah bagian dari belajar.

Selesai

Kalau semua checklist di bagian sebelumnya tercentang, kamu sudah punya aplikasi CRUD lengkap: database, API, antarmuka, dan dokumentasi. Selamat membangun.