Studi Kasus #3: Aplikasi Wisata Indonesia – Membuat Server Express dan Koneksi Database


Studi Kasus #3: Aplikasi Wisata Indonesia – Membuat Server Express dan Koneksi Database

Halo, calon full-stack developer! 

Setelah menyiapkan lingkungan development di Studi Kasus #2, sekarang saatnya kita mulai membangun backend! Kita akan membuat server menggunakan Express.js, menghubungkannya ke database MySQL, dan membuat fungsi-fungsi dasar untuk mengakses data. Server ini akan menjadi "jantung" aplikasi yang melayani permintaan dari frontend React nantinya.

Bayangkan server ini seperti pelayan di restoran. Dia menerima pesanan (request), memasak (memproses data), dan mengirimkan hidangan (response) ke pelanggan (frontend). Yuk, kita buat pelayan kita! 

Langkah 1: Masuk ke Folder Backend dan Inisialisasi

Buka terminal/command prompt, lalu arahkan ke folder proyek utama wisata-indonesia. Masuk ke folder backend yang sudah kita buat sebelumnya:

cd backend

Pastikan di folder ini sudah ada file package.json dari inisialisasi sebelumnya. Jika belum, jalankan:

npm init -y

Sekarang kita akan menginstal semua paket yang dibutuhkan.

Langkah 2: Instalasi Paket yang Diperlukan

Jalankan perintah berikut untuk menginstal paket-paket:

npm install express mysql2 cors dotenv multer

Penjelasan masing-masing paket:

  • express – framework web untuk membuat server dan API.
  • mysql2 – driver MySQL untuk Node.js (mendukung promise).
  • cors – middleware untuk mengizinkan permintaan dari domain lain (agar frontend React bisa mengakses API).
  • dotenv – untuk membaca konfigurasi dari file .env (seperti password database).
  • multer – middleware untuk menangani upload file (nanti kita pakai untuk upload gambar).
💡 Catatan: Jika ingin menginstal sebagai devDependency (hanya untuk development), bisa tambahkan --save-dev. Tapi untuk paket-paket di atas, kita butuh semuanya di production juga.

Setelah selesai, akan ada folder node_modules dan file package-lock.json. Ini normal.

Langkah 3: Membuat File .env untuk Konfigurasi Database

File .env digunakan untuk menyimpan variabel lingkungan seperti kredensial database. Ini penting agar data sensitif tidak tercampur dengan kode.

Buat file baru di folder backend dengan nama .env. Isi dengan konfigurasi berikut (sesuaikan dengan password dan host MySQL kamu):

DB_HOST=localhost
DB_USER=root
DB_PASSWORD=root
DB_NAME=wisata_db
PORT=3000

Jika pakai XAMPP, biasanya password root kosong. Tapi sebaiknya isi dengan password yang sudah kamu set.

⚠️ Penting: Jangan commit file .env ke Git! Tambahkan .env ke file .gitignore.

Buat file .gitignore di folder backend (jika belum ada) dan isi dengan:

node_modules/
.env

Langkah 4: Membuat File Konfigurasi Database

Kita akan membuat file khusus untuk mengelola koneksi database. Buat folder config di dalam backend, lalu buat file db.js.

Struktur folder:

backend/
├── config/
│   └── db.js
├── node_modules/
├── .env
├── .gitignore
├── package.json
└── package-lock.json

Isi file config/db.js:

const mysql = require('mysql2');
require('dotenv').config();

// Buat pool koneksi (lebih efisien daripada koneksi tunggal)
const pool = mysql.createPool({
  host: process.env.DB_HOST,
  user: process.env.DB_USER,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_NAME,
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

// Ubah pool menjadi promise-based agar bisa pakai async/await
const promisePool = pool.promise();

module.exports = promisePool;

Penjelasan:

  • require('dotenv').config() membaca file .env dan memasukkan nilainya ke process.env.
  • mysql.createPool membuat kumpulan koneksi, lebih baik daripada membuat koneksi setiap kali.
  • pool.promise() mengembalikan versi promise dari pool, sehingga kita bisa menggunakan async/await.
  • Kita export promisePool agar bisa digunakan di file lain.

Langkah 5: Membuat Server Express

Buat file app.js di folder backend. Ini akan menjadi file utama server kita.

const express = require('express');
const cors = require('cors');
require('dotenv').config();

const app = express();
const port = process.env.PORT || 3000;

// Middleware
app.use(cors()); // mengizinkan akses dari frontend
app.use(express.json()); // membaca request body dalam format JSON
app.use(express.urlencoded({ extended: true })); // membaca form-urlencoded

// Route sederhana untuk testing
app.get('/', (req, res) => {
  res.json({ message: 'Server Wisata Indonesia berjalan!' });
});

// Jalankan server
app.listen(port, () => {
  console.log(`🚀 Server berjalan di http://localhost:${port}`);
});

Jalankan server dengan:

node app.js

Buka browser ke http://localhost:3000. Harusnya muncul JSON:

{ "message": "Server Wisata Indonesia berjalan!" }

Selamat! Server pertama kita sudah hidup. 🎉

Langkah 6: Membuat Fungsi Query Dasar (Contoh: Categories)

Sekarang kita akan membuat fungsi untuk mengambil data dari tabel categories. Buat folder controllers dan routes untuk menjaga kode tetap rapi.

Pertama, buat file controllers/categoryController.js:

const db = require('../config/db');

// Mendapatkan semua kategori
const getAllCategories = async (req, res) => {
  try {
    const [rows] = await db.query('SELECT * FROM categories ORDER BY id DESC');
    res.json(rows);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Gagal mengambil data kategori' });
  }
};

// Mendapatkan satu kategori berdasarkan ID
const getCategoryById = async (req, res) => {
  const { id } = req.params;
  try {
    const [rows] = await db.query('SELECT * FROM categories WHERE id = ?', [id]);
    if (rows.length === 0) {
      return res.status(404).json({ error: 'Kategori tidak ditemukan' });
    }
    res.json(rows[0]);
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Gagal mengambil data kategori' });
  }
};

// Menambah kategori baru
const createCategory = async (req, res) => {
  const { name, slug, description } = req.body;
  if (!name || !slug) {
    return res.status(400).json({ error: 'Nama dan slug harus diisi' });
  }
  try {
    const [result] = await db.query(
      'INSERT INTO categories (name, slug, description) VALUES (?, ?, ?)',
      [name, slug, description]
    );
    res.status(201).json({ id: result.insertId, name, slug, description });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Gagal menambah kategori' });
  }
};

// Update kategori
const updateCategory = async (req, res) => {
  const { id } = req.params;
  const { name, slug, description } = req.body;
  try {
    const [result] = await db.query(
      'UPDATE categories SET name = ?, slug = ?, description = ? WHERE id = ?',
      [name, slug, description, id]
    );
    if (result.affectedRows === 0) {
      return res.status(404).json({ error: 'Kategori tidak ditemukan' });
    }
    res.json({ message: 'Kategori berhasil diupdate' });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Gagal mengupdate kategori' });
  }
};

// Hapus kategori
const deleteCategory = async (req, res) => {
  const { id } = req.params;
  try {
    const [result] = await db.query('DELETE FROM categories WHERE id = ?', [id]);
    if (result.affectedRows === 0) {
      return res.status(404).json({ error: 'Kategori tidak ditemukan' });
    }
    res.json({ message: 'Kategori berhasil dihapus' });
  } catch (err) {
    console.error(err);
    res.status(500).json({ error: 'Gagal menghapus kategori' });
  }
};

module.exports = {
  getAllCategories,
  getCategoryById,
  createCategory,
  updateCategory,
  deleteCategory
};

Penjelasan:

  • Kita menggunakan await db.query() untuk menjalankan query SQL. Hasilnya adalah array dua elemen: [rows, fields]. Kita ambil rows.
  • Query parameter menggunakan placeholder ? untuk menghindari SQL injection.
  • Error handling dengan try-catch, dan mengirim respons JSON yang sesuai.

Selanjutnya, buat file routes/categoryRoutes.js untuk mendefinisikan endpoint:

const express = require('express');
const router = express.Router();
const categoryController = require('../controllers/categoryController');

router.get('/', categoryController.getAllCategories);
router.get('/:id', categoryController.getCategoryById);
router.post('/', categoryController.createCategory);
router.put('/:id', categoryController.updateCategory);
router.delete('/:id', categoryController.deleteCategory);

module.exports = router;

Terakhir, kita daftarkan route tersebut di app.js.

Tambahkan baris berikut di app.js setelah middleware dan sebelum app.listen:

const categoryRoutes = require('./routes/categoryRoutes');
app.use('/api/categories', categoryRoutes);

Jadi, semua endpoint kategori akan memiliki prefix /api/categories.

Langkah 7: Menguji API dengan Postman

Pastikan server sudah berjalan (dengan node app.js). Buka Postman dan coba beberapa request:

  • GET http://localhost:3000/api/categories – mengambil semua kategori (harusnya array kosong atau data dari Studi Kasus #1).
  • POST http://localhost:3000/api/categories dengan body JSON (pilih raw JSON):
    {
      "name": "Wisata Alam",
      "slug": "wisata-alam",
      "description": "Tempat wisata alam seperti pantai, gunung, dll."
    }
  • GET lagi untuk memastikan data sudah masuk.
  • GET http://localhost:3000/api/categories/1 – mengambil kategori dengan ID 1.
  • PUT http://localhost:3000/api/categories/1 dengan body JSON untuk update.
  • DELETE http://localhost:3000/api/categories/1 untuk menghapus.

Pastikan semua berjalan dengan baik. Jika ada error, periksa kembali kode atau koneksi database.

📁 Struktur Folder Backend Saat Ini

backend/
├── config/
│   └── db.js
├── controllers/
│   └── categoryController.js
├── routes/
│   └── categoryRoutes.js
├── node_modules/
├── .env
├── .gitignore
├── app.js
├── package.json
└── package-lock.json

Penjelasan Tambahan

  • Mengapa menggunakan pool? – Pool lebih efisien karena koneksi MySQL dapat digunakan kembali. Tanpa pool, setiap request akan membuat koneksi baru dan menutupnya, yang lambat.
  • Mengapa query parameter menggunakan ?? – Untuk mencegah SQL injection, jangan pernah menggabungkan nilai langsung ke string SQL.
  • Middleware cors: Frontend React yang berjalan di domain berbeda (misal localhost:5173) akan bisa mengakses API ini.
  • Middleware express.json: Memungkinkan kita membaca JSON dari body request.

Langkah Selanjutnya

Di Studi Kasus #4, kita akan membuat API untuk tabel-tabel lain (destinations, accommodations, transportations) dan mengelola relasi dengan kategori. Kita juga akan mulai menggunakan multer untuk upload gambar.

Pastikan server berjalan dan semua endpoint kategori berfungsi sebelum lanjut. Jika ada kendala, tulis di komentar!

Sampai jumpa di tutorial berikutnya! 👋😊


Ditulis dengan ❤️ untuk para pengembang yang ingin membangun aplikasi wisata.

Lebih baru Lebih lama

نموذج الاتصال