Belajar Spring Boot dan React JS dari Nol: Tutorial CRUD Lengkap untuk Pemula
Membangun aplikasi web modern tidak harus dimulai dengan teknologi yang rumit. Jika Anda sudah memahami dasar pemrograman Java, seperti class, object, method, dan variabel, sebenarnya Anda sudah memiliki modal yang cukup untuk mulai belajar pengembangan aplikasi web.
Dalam tutorial ini, kita akan membangun aplikasi Manajemen Produk menggunakan Spring Boot sebagai backend dan React JS sebagai frontend.
Tutorial ini ditulis untuk pemula. Kita tidak akan langsung melompat ke penulisan kode, tetapi memulai dari instalasi software, pengenalan konsep, pembuatan project, sampai aplikasi CRUD benar-benar dapat digunakan.
1. Apa yang Akan Kita Buat?
Kita akan membuat aplikasi sederhana untuk mengelola data produk dengan empat kemampuan utama:
- Create: Menambahkan produk baru.
- Read: Melihat daftar produk.
- Update: Mengubah informasi produk.
- Delete: Menghapus produk.
Setiap produk memiliki tiga atribut:
| Atribut | Contoh |
|---|---|
| ID | 1 |
| Nama Produk | Laptop Asus |
| Harga | 8500000 |
Aplikasi akan memiliki sebuah form untuk memasukkan nama dan harga produk, serta tabel yang menampilkan seluruh produk yang tersimpan.
Ketika pengguna menekan tombol Simpan, React akan mengirim data ke Spring Boot. Spring Boot kemudian memproses dan menyimpan data tersebut ke database.
2. Mengenal Teknologi yang Digunakan
Sebelum menulis kode, penting untuk memahami fungsi masing-masing teknologi.
2.1 Java
Java adalah bahasa pemrograman yang akan kita gunakan untuk membuat backend.
Karena tutorial ini ditujukan bagi pembaca yang sudah memahami Java dasar, kita tidak akan membahas kembali konsep variabel, class, dan method secara mendalam.
2.2 Spring Boot
Spring Boot adalah framework berbasis Java yang mempermudah pembuatan aplikasi backend.
Dengan Spring Boot, kita tidak perlu mengonfigurasi web server secara manual untuk menjalankan REST API sederhana.
Spring Boot menyediakan berbagai fitur seperti:
- Embedded web server.
- Dependency Injection.
- REST API.
- Integrasi database.
- Validasi data.
- Pengelolaan konfigurasi aplikasi.
2.3 React JS
React adalah library JavaScript untuk membangun antarmuka pengguna.
Jika Spring Boot menangani data dan logika backend, React menangani apa yang dilihat dan digunakan oleh pengguna di browser.
2.4 Database H2
H2 adalah database ringan yang dapat digunakan langsung dalam aplikasi Java.
Untuk pembelajaran awal, kita menggunakan H2 agar tidak perlu memasang PostgreSQL atau MySQL terlebih dahulu.
Nantinya, konsep yang sama dapat diterapkan menggunakan database lain.
2.5 IntelliJ IDEA dan Visual Studio Code
Kita akan menggunakan dua editor:
- IntelliJ IDEA: Mengembangkan backend Spring Boot.
- Visual Studio Code: Mengembangkan frontend React JS.
Penggunaan dua editor bukan suatu kewajiban. Namun, untuk pembelajaran ini, pemisahan tersebut membantu memahami bahwa frontend dan backend adalah dua aplikasi yang berbeda.
BAGIAN I — PERSIAPAN LINGKUNGAN PENGEMBANGAN
3. Instalasi Java Development Kit
Spring Boot membutuhkan Java Development Kit atau JDK.
Dalam tutorial ini kita menggunakan Java 21 LTS.
Langkah Instalasi
- Buka https://adoptium.net/
- Pilih Eclipse Temurin JDK 21.
- Pilih sistem operasi Windows dan arsitektur x64.
- Download installer.
- Jalankan installer dan ikuti petunjuk.
- Aktifkan pengaturan
JAVA_HOMEdanPATHjika tersedia.
Setelah instalasi selesai, buka Command Prompt.
Jalankan:
java -version
Contoh hasil:
openjdk version "21.0.x"
Periksa juga Java compiler:
javac -version
Contoh:
javac 21.0.x
Jika kedua perintah berhasil, Java siap digunakan.
Catatan: Jika muncul pesan java is not recognized, tutup Command Prompt dan buka kembali. Jika masih gagal, periksa konfigurasi Environment Variables Windows.
4. Instalasi IntelliJ IDEA
IntelliJ IDEA merupakan IDE yang sangat membantu pengembangan aplikasi Java.
Download melalui:
https://www.jetbrains.com/idea/download/
Lakukan instalasi seperti aplikasi Windows pada umumnya.
Untuk tutorial ini, fitur dasar IntelliJ sudah cukup. Kita tidak memerlukan fitur Spring khusus yang berbayar.
5. Instalasi Node.js
Node.js dibutuhkan untuk menjalankan tooling frontend React.
Download dari:
Pilih versi LTS yang kompatibel dengan Vite.
Setelah instalasi, buka Command Prompt:
node -v
Kemudian:
npm -v
Jika keduanya menampilkan nomor versi, instalasi berhasil.
Apa Perbedaan Node.js dan npm?
Node.js memungkinkan JavaScript dijalankan di luar browser.
Sementara itu, npm adalah package manager yang digunakan untuk mengunduh dan mengelola library JavaScript.
Sebagai analogi:
- Maven digunakan untuk mengelola dependency Java.
- npm digunakan untuk mengelola dependency JavaScript.
6. Instalasi Visual Studio Code
Download melalui:
https://code.visualstudio.com/
Setelah terpasang, buka menu Extensions dengan shortcut:
Ctrl + Shift + X
Instal beberapa extension berikut:
- ESLint.
- Prettier.
- ES7+ React/Redux/React-Native Snippets.
ESLint membantu mendeteksi masalah kode, Prettier merapikan penulisan, sedangkan React Snippets menyediakan shortcut penulisan komponen.
7. Membuat Folder Project
Buat folder berikut:
C:\
└── belajar-spring-react\
├── backend\
└── frontend\
Kita akan menggunakan:
backenduntuk Spring Boot.frontenduntuk React.
Kedua aplikasi nantinya dijalankan secara terpisah ketika development.
BAGIAN II — MEMBUAT BACKEND SPRING BOOT
8. Membuat Project Spring Boot
Buka:
Spring Initializr adalah layanan yang membantu menghasilkan struktur awal project Spring Boot.
Isi pengaturan berikut:
| Pengaturan | Nilai |
|---|---|
| Project | Maven |
| Language | Java |
| Spring Boot | Versi stabil 3.x |
| Group | com.belajar |
| Artifact | backend |
| Name | backend |
| Package Name | com.belajar.backend |
| Packaging | Jar |
| Java | 21 |
Tutorial ini menggunakan keluarga Spring Boot 3.x agar import dan konfigurasi tetap konsisten.
Tambahkan dependency:
- Spring Web.
- Spring Data JPA.
- H2 Database.
- Validation.
Klik Generate.
File backend.zip akan diunduh.
Ekstrak isinya ke:
C:\belajar-spring-react\backend
Pastikan file pom.xml berada langsung di folder backend.
9. Membuka Project di IntelliJ
Jalankan IntelliJ IDEA.
Pilih:
File → Open
Kemudian pilih:
C:\belajar-spring-react\backend
Tunggu sampai IntelliJ menyelesaikan proses import Maven dan pengunduhan dependency.
Mengenal Struktur Project
backend/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/belajar/backend/
│ │ │ └── BackendApplication.java
│ │ └── resources/
│ │ └── application.properties
│ └── test/
├── pom.xml
├── mvnw
└── mvnw.cmd
Beberapa file penting:
pom.xml
File konfigurasi Maven. Di dalamnya terdapat informasi project dan dependency.
BackendApplication.java
Class utama yang digunakan untuk menjalankan aplikasi Spring Boot.
application.properties
File konfigurasi aplikasi, seperti port server dan koneksi database.
mvnw.cmd
Maven Wrapper untuk Windows. Dengan file ini, kita dapat menjalankan perintah Maven tanpa harus memasang Maven secara terpisah.
10. Memahami Class Utama Spring Boot
Buka BackendApplication.java.
package com.belajar.backend;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(
BackendApplication.class,
args
);
}
}
Bagi yang sudah memahami Java, method berikut tentu familiar:
public static void main(String[] args)
Method ini merupakan titik awal aplikasi.
Perbedaannya, pada Spring Boot kita menjalankan:
SpringApplication.run(
BackendApplication.class,
args
);
Perintah tersebut menginisialisasi Spring Application Context, memuat konfigurasi, dan menjalankan web server yang dibutuhkan aplikasi.
Apa Itu Annotation?
Perhatikan:
@SpringBootApplication
Annotation merupakan metadata yang digunakan framework untuk menentukan bagaimana suatu class atau method diperlakukan.
Spring menggunakan annotation secara luas untuk mendefinisikan controller, service, entity, dan konfigurasi.
11. Menjalankan Spring Boot Pertama Kali
Pada IntelliJ, buka BackendApplication.java.
Klik tombol Run di samping method main().
Alternatif melalui terminal:
cd C:\belajar-spring-react\backend
.\mvnw.cmd spring-boot:run
Jika berhasil, akan muncul log serupa:
Tomcat started on port 8080
Started BackendApplication
Buka browser:
Jika muncul halaman error 404, jangan khawatir. Server sudah berjalan, tetapi kita belum membuat endpoint untuk alamat utama.
12. Membuat REST API Pertama
Sebelum membuat CRUD, kita akan membuat endpoint sederhana.
Pada package com.belajar.backend, buat package baru bernama:
controller
Di dalamnya, buat class:
HelloController.java
Isi:
package com.belajar.backend.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Halo dari Spring Boot!";
}
}
Jalankan kembali aplikasi jika diperlukan.
Buka:
Hasilnya:
Halo dari Spring Boot!
Penjelasan
@RestController menandakan bahwa class menangani permintaan REST API.
@GetMapping("/hello") menghubungkan permintaan HTTP GET pada URL /hello dengan method hello().
Ketika URL tersebut dibuka, Spring Boot menjalankan method dan mengirim hasilnya sebagai HTTP response.
BAGIAN III — DATABASE DAN CRUD BACKEND
13. Konfigurasi Database
Buka:
src/main/resources/application.properties
Isi dengan:
spring.application.name=backend
server.port=8080
# Database H2 berbasis file
spring.datasource.url=jdbc:h2:file:./data/produkdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
# Hibernate
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
# H2 Console
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
Penjelasan
server.port=8080 menentukan port backend.
spring.datasource.url menentukan lokasi database H2.
Karena menggunakan jdbc:h2:file, data akan disimpan dalam file sehingga tetap tersedia setelah aplikasi dihentikan dan dijalankan kembali.
spring.jpa.hibernate.ddl-auto=update mengizinkan Hibernate memperbarui struktur tabel berdasarkan entity Java.
Konfigurasi tersebut cocok untuk belajar, tetapi pada production sebaiknya menggunakan migrasi database yang terkontrol seperti Flyway atau Liquibase.
spring.jpa.show-sql=true menampilkan SQL Hibernate di terminal.
Penting: H2 Console dan konfigurasi database tanpa password dalam tutorial ini hanya untuk lingkungan development lokal.
14. Membuat Entity Product
Buat package:
model
Kemudian buat class:
Product.java
Isi:
package com.belajar.backend.model;
import jakarta.persistence.*;
import jakarta.validation.constraints.DecimalMin;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
@Entity
@Table(name = "products")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@NotBlank(message = "Nama wajib diisi")
@Column(nullable = false)
private String nama;
@NotNull(message = "Harga wajib diisi")
@DecimalMin(
value = "0.0",
message = "Harga tidak boleh negatif"
)
@Column(
nullable = false,
precision = 19,
scale = 2
)
private BigDecimal harga;
public Product() {
}
public Long getId() {
return id;
}
public String getNama() {
return nama;
}
public void setNama(String nama) {
this.nama = nama;
}
public BigDecimal getHarga() {
return harga;
}
public void setHarga(BigDecimal harga) {
this.harga = harga;
}
}
Memahami Entity
@Entity memberi tahu JPA bahwa class ini merepresentasikan data database.
@Table(name = "products") menentukan nama tabel.
@Id menunjukkan primary key.
@GeneratedValue mengatur pembuatan ID otomatis.
@NotBlank digunakan untuk memvalidasi nama produk.
@DecimalMin digunakan untuk memastikan harga tidak negatif.
Mengapa Menggunakan BigDecimal?
Dalam Java, double menggunakan representasi floating-point biner yang tidak selalu dapat merepresentasikan nilai desimal secara tepat.
Untuk nilai uang, BigDecimal lebih sesuai karena mendukung representasi desimal dan aturan pembulatan yang eksplisit.
15. Membuat Repository
Buat package:
repository
Kemudian buat interface:
ProductRepository.java
Isi:
package com.belajar.backend.repository;
import com.belajar.backend.model.Product;
import org.springframework.data.jpa.repository.JpaRepository;
public interface ProductRepository
extends JpaRepository<Product, Long> {
}
Mungkin muncul pertanyaan: mengapa interface ini tidak memiliki method?
Karena JpaRepository sudah menyediakan operasi dasar seperti:
findAll()
findById(id)
save(product)
delete(product)
existsById(id)
Spring Data JPA akan menyediakan implementasinya secara otomatis.
Kita tidak perlu menulis SQL untuk setiap operasi CRUD sederhana.
16. Membuat Service
Buat package:
service
Kemudian buat class:
ProductService.java
Isi:
package com.belajar.backend.service;
import com.belajar.backend.model.Product;
import com.belajar.backend.repository.ProductRepository;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.server.ResponseStatusException;
import java.util.List;
@Service
public class ProductService {
private final ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public List<Product> getAllProducts() {
return repository.findAll();
}
public Product getProductById(Long id) {
return repository.findById(id)
.orElseThrow(() ->
new ResponseStatusException(
HttpStatus.NOT_FOUND,
"Produk tidak ditemukan"
)
);
}
public Product createProduct(Product product) {
return repository.save(product);
}
public Product updateProduct(Long id, Product data) {
Product product = getProductById(id);
product.setNama(data.getNama());
product.setHarga(data.getHarga());
return repository.save(product);
}
public void deleteProduct(Long id) {
Product product = getProductById(id);
repository.delete(product);
}
}
Mengapa Menggunakan Service?
Secara teknis, Controller dapat memanggil Repository secara langsung.
Namun, pemisahan Service membuat aplikasi lebih mudah dikembangkan.
Misalnya, suatu hari kita ingin menambahkan aturan:
Harga produk tidak boleh lebih rendah dari harga modal.
Aturan tersebut dapat ditempatkan di Service tanpa mencampurkannya dengan kode HTTP pada Controller.
Memahami Dependency Injection
Perhatikan constructor:
public ProductService(ProductRepository repository) {
this.repository = repository;
}
Kita tidak membuat object Repository secara manual.
Spring akan menyediakan dependency yang dibutuhkan ketika membuat object ProductService.
Konsep ini disebut Dependency Injection.
17. Membuat Controller CRUD
Buat class:
controller/ProductController.java
Isi:
package com.belajar.backend.controller;
import com.belajar.backend.model.Product;
import com.belajar.backend.service.ProductService;
import jakarta.validation.Valid;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
@CrossOrigin(origins = "http://localhost:5173")
public class ProductController {
private final ProductService service;
public ProductController(ProductService service) {
this.service = service;
}
@GetMapping
public List<Product> getAllProducts() {
return service.getAllProducts();
}
@GetMapping("/{id}")
public Product getProductById(
@PathVariable Long id) {
return service.getProductById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public Product createProduct(
@Valid @RequestBody Product product) {
return service.createProduct(product);
}
@PutMapping("/{id}")
public Product updateProduct(
@PathVariable Long id,
@Valid @RequestBody Product product) {
return service.updateProduct(id, product);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteProduct(
@PathVariable Long id) {
service.deleteProduct(id);
}
}
Mengenal HTTP Method
| Method | Fungsi | Endpoint |
|---|---|---|
| GET | Mengambil seluruh produk | /api/products |
| GET | Mengambil satu produk | /api/products/1 |
| POST | Menambahkan produk | /api/products |
| PUT | Memperbarui produk | /api/products/1 |
| DELETE | Menghapus produk | /api/products/1 |
Apa Itu @RequestBody?
Annotation ini digunakan untuk membaca JSON yang dikirim melalui HTTP request.
Misalnya React mengirim:
{
"nama": "Laptop Asus",
"harga": 8500000
}
Spring akan mengubah JSON tersebut menjadi object Java Product.
Apa Itu @PathVariable?
Misalnya kita mengakses:
/api/products/5
Angka 5 akan dibaca melalui:
@PathVariable Long id
Dengan demikian, Spring mengetahui produk mana yang diminta.
Apa Itu CORS?
React akan berjalan pada port 5173, sedangkan Spring Boot berjalan pada port 8080.
Karena origin keduanya berbeda, browser menerapkan aturan Cross-Origin Resource Sharing atau CORS.
Annotation berikut mengizinkan frontend lokal kita mengakses API:
@CrossOrigin(origins = "http://localhost:5173")
CORS bukan mekanisme login atau pengamanan API. Pada aplikasi production, keamanan tetap membutuhkan autentikasi dan otorisasi yang sesuai.
18. Struktur Akhir Backend
Setelah semua class dibuat, struktur backend adalah:
backend/
└── src/
└── main/
├── java/
│ └── com/belajar/backend/
│ ├── BackendApplication.java
│ ├── controller/
│ │ ├── HelloController.java
│ │ └── ProductController.java
│ ├── model/
│ │ └── Product.java
│ ├── repository/
│ │ └── ProductRepository.java
│ └── service/
│ └── ProductService.java
└── resources/
└── application.properties
19. Menguji Backend
Jalankan aplikasi melalui IntelliJ.
Buka browser:
http://localhost:8080/api/products
Jika belum ada data, responsnya:
[]
Ini berarti API berhasil dijalankan.
Menambahkan Produk Menggunakan PowerShell
Buka PowerShell:
$body = @{
nama = "Laptop Asus"
harga = 8500000
} | ConvertTo-Json
Invoke-RestMethod `
-Uri "http://localhost:8080/api/products" `
-Method Post `
-ContentType "application/json" `
-Body $body
Contoh respons:
{
"id": 1,
"nama": "Laptop Asus",
"harga": 8500000.00
}
Melihat Seluruh Produk
Invoke-RestMethod `
-Uri "http://localhost:8080/api/products"
Mengubah Produk
$body = @{
nama = "Laptop Asus Gaming"
harga = 9500000
} | ConvertTo-Json
Invoke-RestMethod `
-Uri "http://localhost:8080/api/products/1" `
-Method Put `
-ContentType "application/json" `
-Body $body
Menghapus Produk
Invoke-RestMethod `
-Uri "http://localhost:8080/api/products/1" `
-Method Delete
Pastikan ID yang digunakan memang tersedia di database.
Jika seluruh pengujian berhasil, backend CRUD kita sudah selesai.
20. Melihat Database H2
Buka:
http://localhost:8080/h2-console
Masukkan:
JDBC URL : jdbc:h2:file:./data/produkdb
Username : sa
Password :
Biarkan password kosong sesuai konfigurasi tutorial.
Klik Connect.
Jalankan SQL:
SELECT * FROM PRODUCTS;
Anda akan melihat isi tabel produk.
Perlu diperhatikan bahwa path database bersifat relatif terhadap working directory aplikasi. Untuk menghindari kebingungan, jalankan backend dari folder project yang sama setiap kali.
BAGIAN IV — MEMBUAT FRONTEND REACT JS
21. Mengenal React JS
React menggunakan konsep komponen.
Komponen adalah bagian antarmuka yang dapat digunakan kembali.
Sebagai contoh, sebuah aplikasi dapat terdiri dari:
App
├── Header
├── ProductForm
├── ProductList
└── Footer
Untuk tutorial pertama, kita akan menggunakan satu komponen App agar alur program lebih mudah diikuti.
Setelah memahami dasarnya, barulah aplikasi dapat dipecah menjadi beberapa komponen.
22. Membuat Project React dengan Vite
Buka Command Prompt.
Masuk ke folder project:
cd C:\belajar-spring-react
Jalankan:
npm create vite@latest frontend -- --template react
Jika muncul pertanyaan konfirmasi, pilih opsi yang sesuai untuk melanjutkan pembuatan project.
Masuk ke folder frontend:
cd frontend
Instal dependency:
npm install
Kemudian jalankan:
npm run dev
Jika berhasil, terminal akan menampilkan alamat seperti:
http://localhost:5173/
Buka alamat tersebut melalui browser.
Halaman React bawaan Vite akan ditampilkan.
Jika port 5173 tidak tersedia, Vite dapat memilih port lain. Untuk tutorial ini, gunakan port 5173 agar sesuai dengan konfigurasi CORS backend.
23. Membuka Project React di VS Code
Buka Visual Studio Code.
Pilih:
File → Open Folder
Pilih:
C:\belajar-spring-react\frontend
Struktur project kurang lebih:
frontend/
├── node_modules/
├── public/
├── src/
│ ├── assets/
│ ├── App.css
│ ├── App.jsx
│ ├── index.css
│ └── main.jsx
├── index.html
├── package.json
└── vite.config.js
Mengenal File React
main.jsx
Titik awal React.
App.jsx
Komponen utama aplikasi.
App.css
Styling untuk tampilan App.
index.css
Styling global aplikasi.
package.json
Konfigurasi package dan script npm.
24. Memahami JSX
Pada JavaScript biasa, kita mungkin menulis:
function hello() {
return "Halo Dunia";
}
Di React, kita dapat menulis:
function App() {
return <h1>Halo Dunia</h1>;
}
export default App;
Penulisan HTML di dalam JavaScript tersebut disebut JSX.
JSX bukan HTML murni. JSX merupakan sintaks yang diproses oleh tooling React menjadi representasi elemen antarmuka.
25. Memahami useState
Sebelum membuat CRUD, kita perlu memahami konsep state.
Contoh:
import { useState } from "react";
function Counter() {
const [count, setCount] = useState(0);
return (
<div>
<p>Nilai: {count}</p>
<button onClick={() => setCount(count + 1)}>
Tambah
</button>
</div>
);
}
export default Counter;
Perhatikan:
const [count, setCount] = useState(0);
count menyimpan nilai state saat render berlangsung.
setCount digunakan untuk meminta React memperbarui nilai tersebut.
0 adalah nilai awal.
Ketika tombol ditekan, React akan memperbarui state dan merender ulang komponen sehingga nilai baru tampil.
Apa Itu const?
Dalam JavaScript:
const nama = "Laptop";
const berarti binding variabel tidak dapat diisi ulang dengan nilai lain.
Sementara:
let harga = 100000;
let memungkinkan variabel diberi nilai baru.
Dalam React, const sangat sering digunakan karena nilai state diperbarui melalui setter, bukan dengan mengubah variabel secara langsung.
26. Memahami useEffect
React menyediakan Hook useEffect untuk menyinkronkan komponen dengan sistem eksternal, termasuk pengambilan data dari API.
Contoh sederhana:
useEffect(() => {
console.log("Effect dijalankan");
}, []);
Array kosong [] berarti Effect tidak dijalankan ulang akibat perubahan dependency selama komponen tetap terpasang.
Pada development dengan React Strict Mode, Effect dapat dijalankan lebih dari sekali untuk membantu mendeteksi masalah. Karena itu, pengambilan data sebaiknya dirancang agar aman terhadap pengulangan.
BAGIAN V — MEMBANGUN CRUD REACT
27. Membuat App.jsx
Buka:
src/App.jsx
Hapus seluruh isi bawaan, kemudian ganti dengan kode berikut.
import { useEffect, useState } from "react";
import "./App.css";
const API_URL = "http://localhost:8080/api/products";
function App() {
// State untuk daftar produk
const [products, setProducts] = useState([]);
// State untuk form
const [nama, setNama] = useState("");
const [harga, setHarga] = useState("");
// ID produk yang sedang diedit
const [editId, setEditId] = useState(null);
// State untuk informasi proses
const [loading, setLoading] = useState(false);
const [error, setError] = useState("");
const [message, setMessage] = useState("");
// READ: Mengambil data produk
async function loadProducts() {
try {
const response = await fetch(API_URL);
if (!response.ok) {
throw new Error("Gagal mengambil data produk");
}
const data = await response.json();
setProducts(data);
} catch (err) {
setError(err.message);
}
}
// Memuat data saat komponen ditampilkan
useEffect(() => {
loadProducts();
}, []);
// Membersihkan form
function resetForm() {
setNama("");
setHarga("");
setEditId(null);
}
// CREATE dan UPDATE
async function handleSubmit(event) {
event.preventDefault();
if (!nama.trim()) {
setError("Nama produk wajib diisi");
return;
}
if (
harga === "" ||
!Number.isFinite(Number(harga)) ||
Number(harga) < 0
) {
setError("Harga harus berupa angka tidak negatif");
return;
}
const product = {
nama: nama.trim(),
harga: Number(harga)
};
const isEditing = editId !== null;
const url = isEditing
? `${API_URL}/${editId}`
: API_URL;
const method = isEditing ? "PUT" : "POST";
try {
setLoading(true);
setError("");
setMessage("");
const response = await fetch(url, {
method: method,
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(product)
});
if (!response.ok) {
throw new Error("Gagal menyimpan produk");
}
resetForm();
await loadProducts();
setMessage(
isEditing
? "Produk berhasil diperbarui"
: "Produk berhasil ditambahkan"
);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
// Memilih produk untuk diedit
function handleEdit(product) {
setEditId(product.id);
setNama(product.nama);
setHarga(String(product.harga));
setError("");
setMessage("");
}
// DELETE
async function handleDelete(id) {
const confirmed = window.confirm(
"Yakin ingin menghapus produk ini?"
);
if (!confirmed) {
return;
}
try {
setLoading(true);
setError("");
setMessage("");
const response = await fetch(
`${API_URL}/${id}`,
{
method: "DELETE"
}
);
if (!response.ok) {
throw new Error("Gagal menghapus produk");
}
if (editId === id) {
resetForm();
}
await loadProducts();
setMessage("Produk berhasil dihapus");
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
}
// Format angka menjadi Rupiah
function formatRupiah(value) {
return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
maximumFractionDigits: 0
}).format(value);
}
return (
<div className="container">
<h1>Manajemen Produk</h1>
<p className="subtitle">
CRUD Spring Boot + React JS
</p>
<div className="card">
<h2>
{editId !== null
? "Edit Produk"
: "Tambah Produk"}
</h2>
<form onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="nama">
Nama Produk
</label>
<input
id="nama"
type="text"
value={nama}
onChange={(e) => setNama(e.target.value)}
placeholder="Contoh: Laptop Asus"
required
/>
</div>
<div className="form-group">
<label htmlFor="harga">
Harga Produk
</label>
<input
id="harga"
type="number"
min="0"
step="0.01"
value={harga}
onChange={(e) => setHarga(e.target.value)}
placeholder="Contoh: 8500000"
required
/>
</div>
<div className="actions">
<button
type="submit"
disabled={loading}
>
{loading
? "Memproses..."
: editId !== null
? "Update Produk"
: "Simpan Produk"}
</button>
{editId !== null && (
<button
type="button"
className="secondary"
onClick={resetForm}
>
Batal
</button>
)}
</div>
</form>
</div>
{error && (
<p className="alert error">
{error}
</p>
)}
{message && (
<p className="alert success">
{message}
</p>
)}
<div className="card">
<h2>Daftar Produk</h2>
<div className="table-wrapper">
<table>
<thead>
<tr>
<th>ID</th>
<th>Nama Produk</th>
<th>Harga</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
{products.length === 0 ? (
<tr>
<td colSpan="4">
Belum ada produk
</td>
</tr>
) : (
products.map((product) => (
<tr key={product.id}>
<td>{product.id}</td>
<td>{product.nama}</td>
<td>
{formatRupiah(product.harga)}
</td>
<td>
<div className="actions">
<button
className="edit"
disabled={loading}
onClick={() => handleEdit(product)}
>
Edit
</button>
<button
className="delete"
disabled={loading}
onClick={() => handleDelete(product.id)}
>
Hapus
</button>
</div>
</td>
</tr>
))
)}
</tbody>
</table>
</div>
</div>
</div>
);
}
export default App;
28. Memahami Cara React Berkomunikasi dengan Backend
Perhatikan kode:
const API_URL = "http://localhost:8080/api/products";
Variabel ini menyimpan alamat REST API Spring Boot.
Kemudian:
const response = await fetch(API_URL);
fetch() adalah fungsi JavaScript yang digunakan untuk melakukan HTTP request.
Secara default, fetch() menggunakan metode GET.
Artinya, kode tersebut akan mengirim:
GET /api/products
Ke Spring Boot.
Apa Itu async dan await?
Pada JavaScript, operasi HTTP membutuhkan waktu.
Kita tidak mengetahui apakah respons akan datang dalam 10 milidetik atau beberapa detik.
Karena itu digunakan:
async function loadProducts() {
const response = await fetch(API_URL);
}
async menandakan bahwa function bekerja secara asynchronous dan mengembalikan Promise.
await menunggu penyelesaian Promise sebelum melanjutkan baris berikutnya dalam function tersebut, tanpa memblokir seluruh browser.
Mengubah JSON Menjadi Object JavaScript
const data = await response.json();
Perintah ini membaca dan mem-parsing body respons JSON.
Misalnya backend mengirim:
[
{
"id": 1,
"nama": "Laptop Asus",
"harga": 8500000
}
]
Hasil parsing menjadi array object JavaScript yang dapat digunakan React.
Memperbarui State
setProducts(data);
Ketika state diperbarui, React akan merender ulang bagian tampilan yang bergantung pada state tersebut.
29. Memahami Create dan Update
Perhatikan:
const isEditing = editId !== null;
Jika editId bernilai null, artinya pengguna sedang menambahkan produk.
Jika editId memiliki nilai, misalnya 5, artinya pengguna sedang mengedit produk dengan ID 5.
Kemudian:
const method = isEditing ? "PUT" : "POST";
Ini disebut ternary operator.
Bentuk umumnya:
kondisi ? nilaiJikaBenar : nilaiJikaSalah
Dengan demikian:
- Produk baru menggunakan POST.
- Produk yang diedit menggunakan PUT.
30. Memahami JSON.stringify()
Perhatikan:
body: JSON.stringify(product)
Object JavaScript tidak langsung dikirim sebagai JSON string.
JSON.stringify() mengubah object menjadi representasi teks JSON.
Contohnya:
const product = {
nama: "Mouse",
harga: 250000
};
Akan dikirim dalam bentuk JSON:
{
"nama": "Mouse",
"harga": 250000
}
Spring Boot kemudian membaca JSON tersebut melalui @RequestBody.
31. Memahami Delete
Perhatikan:
const response = await fetch(
`${API_URL}/${id}`,
{
method: "DELETE"
}
);
Jika ID produk adalah 3, alamat yang dipanggil menjadi:
http://localhost:8080/api/products/3
Dengan HTTP method:
DELETE
Spring Boot akan menjalankan method Controller yang memiliki annotation @DeleteMapping("/{id}").
BAGIAN VI — MEMBUAT TAMPILAN DENGAN CSS
32. Mengubah App.css
Buka:
src/App.css
Hapus isi bawaan, lalu ganti dengan:
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, sans-serif;
background: #f4f6f9;
color: #263238;
}
.container {
max-width: 960px;
margin: 40px auto;
padding: 0 20px;
}
h1 {
margin-bottom: 6px;
}
h2 {
margin-top: 0;
font-size: 20px;
}
.subtitle {
color: #64748b;
margin-bottom: 30px;
}
.card {
background: white;
padding: 24px;
border-radius: 12px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);
}
.form-group {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 18px;
}
label {
font-weight: bold;
}
input {
padding: 12px;
border: 1px solid #cbd5e1;
border-radius: 6px;
font-size: 15px;
width: 100%;
}
input:focus {
outline: 2px solid #93c5fd;
border-color: #2563eb;
}
.actions {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
button {
background: #2563eb;
color: white;
padding: 10px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
}
button:hover {
opacity: 0.9;
}
button:disabled {
opacity: 0.5;
cursor: not-allowed;
}
button.secondary {
background: #64748b;
}
button.edit {
background: #d97706;
}
button.delete {
background: #dc2626;
}
.table-wrapper {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
}
th,
td {
padding: 14px;
border-bottom: 1px solid #e2e8f0;
text-align: left;
}
th {
background: #f8fafc;
}
.alert {
padding: 14px;
border-radius: 6px;
margin-bottom: 20px;
}
.error {
background: #fee2e2;
color: #991b1b;
}
.success {
background: #dcfce7;
color: #166534;
}
@media (max-width: 600px) {
.container {
margin: 20px auto;
}
.card {
padding: 16px;
}
}
Selanjutnya, buka:
src/index.css
Ganti isinya:
:root {
font-family: Arial, sans-serif;
font-synthesis: none;
text-rendering: optimizeLegibility;
}
body {
min-width: 320px;
}
Sekarang tampilan aplikasi akan lebih rapi, memiliki form, tombol, tabel, dan pesan status.
BAGIAN VII — MENJALANKAN APLIKASI
33. Menjalankan Backend dan Frontend Bersamaan
Aplikasi kita terdiri dari dua proses.
Terminal Pertama: Backend
Buka IntelliJ IDEA.
Jalankan:
BackendApplication.java
Atau gunakan terminal:
cd C:\belajar-spring-react\backend
.\mvnw.cmd spring-boot:run
Backend berjalan pada:
http://localhost:8080
Terminal Kedua: Frontend
Buka terminal VS Code.
Shortcut:
Ctrl + `
Jalankan:
npm run dev
Frontend berjalan pada:
http://localhost:5173
Penting: Kedua proses harus tetap berjalan. Jika backend dihentikan, React tidak dapat mengambil atau menyimpan data produk.
34. Menguji CRUD Melalui Browser
Buka:
Lakukan pengujian berikut.
Pengujian Create
Isi form:
Nama Produk : Laptop Asus
Harga : 8500000
Klik Simpan Produk.
Produk akan muncul pada tabel.
Pengujian Read
Refresh halaman browser.
Jika produk masih tampil, berarti React berhasil mengambil data dari backend.
Pengujian Update
Klik Edit pada produk.
Ubah harga:
9500000
Klik Update Produk.
Harga pada tabel akan berubah.
Pengujian Delete
Klik Hapus.
Browser akan menampilkan konfirmasi.
Pilih OK.
Produk akan dihapus dari database dan tabel diperbarui.
BAGIAN VIII — MEMAHAMI ALUR PROGRAM SECARA UTUH
35. Alur Ketika Menambahkan Produk
Misalnya pengguna memasukkan:
Nama : Keyboard
Harga : 350000
Kemudian menekan Simpan.
Alur yang terjadi:
PENGGUNA
|
| Mengisi form
v
REACT JS
|
| POST /api/products
| JSON:
| {
| "nama": "Keyboard",
| "harga": 350000
| }
v
SPRING BOOT CONTROLLER
|
| createProduct()
v
SPRING BOOT SERVICE
|
| repository.save()
v
SPRING DATA JPA
|
| INSERT
v
H2 DATABASE
|
| Data tersimpan
v
SPRING BOOT
|
| HTTP 201 + JSON
v
REACT JS
|
| Mengambil daftar terbaru
v
TABEL PRODUK DIPERBARUI
Inilah gambaran sederhana arsitektur aplikasi full stack.
Mengapa Perlu Memisahkan Controller, Service, dan Repository?
Karena masing-masing memiliki tanggung jawab berbeda.
Controller
Menangani komunikasi HTTP.
Service
Menangani logika aplikasi.
Repository
Menangani akses data.
Entity
Merepresentasikan data yang disimpan.
Pemisahan ini membuat aplikasi lebih mudah diuji, diperbaiki, dan dikembangkan.
BAGIAN IX — DEBUGGING UNTUK PEMULA
36. Menggunakan Browser Developer Tools
Ketika aplikasi tidak berjalan sesuai harapan, jangan langsung mengubah kode secara acak.
Gunakan Developer Tools browser.
Pada Chrome atau Edge:
F12
Pilih tab Network.
Kemudian lakukan operasi Simpan Produk.
Anda akan melihat request menuju:
http://localhost:8080/api/products
Klik request tersebut.
Periksa:
- Request Method.
- Status Code.
- Request Payload.
- Response.
Contoh status HTTP:
| Status | Arti |
|---|---|
| 200 | Permintaan berhasil |
| 201 | Data berhasil dibuat |
| 204 | Berhasil tanpa response body |
| 400 | Data request tidak valid |
| 404 | Endpoint atau data tidak ditemukan |
| 500 | Terjadi kesalahan server |
Jika muncul 400, periksa JSON dan aturan validasi.
Jika muncul 404, periksa URL endpoint dan ID produk.
Jika muncul 500, periksa log Spring Boot di IntelliJ.
37. Menggunakan Debugger IntelliJ
IntelliJ menyediakan debugger untuk memeriksa alur program Java.
Contohnya, buka:
public Product createProduct(Product product) {
return repository.save(product);
}
Klik area di sebelah kiri nomor baris pada:
return repository.save(product);
Sebuah titik merah akan muncul. Ini disebut breakpoint.
Kemudian:
- Hentikan aplikasi jika masih berjalan.
- Jalankan melalui tombol Debug di IntelliJ.
- Buka React.
- Tambahkan produk baru.
- IntelliJ akan menghentikan eksekusi sementara pada breakpoint.
Sekarang Anda dapat melihat nilai object product, termasuk nama dan harga.
Debugger sangat berguna untuk memahami bagaimana data berpindah dari React menuju Java.
38. Error yang Sering Ditemukan
Error: Port 8080 Already in Use
Artinya port 8080 sudah digunakan proses lain.
Pada Windows:
netstat -ano | findstr :8080
Periksa PID proses yang menggunakan port tersebut.
Jika proses itu memang milik Anda dan aman dihentikan, gunakan:
taskkill /PID NOMOR_PID /F
Ganti NOMOR_PID dengan PID yang benar. Jangan menghentikan proses yang tidak dikenali.
Alternatifnya, ubah port backend pada application.properties, tetapi alamat API React juga harus disesuaikan.
Error: Failed to Fetch
Biasanya React tidak berhasil menghubungi backend.
Periksa:
- Apakah Spring Boot berjalan?
- Apakah port backend benar?
- Apakah endpoint
/api/productsbisa dibuka? - Apakah browser menampilkan masalah CORS?
- Apakah ada error jaringan di tab Network?
Error: CORS Policy
Pastikan Controller memiliki:
@CrossOrigin(origins = "http://localhost:5173")
Perhatikan bahwa port harus sesuai dengan alamat frontend yang benar-benar digunakan.
Error: npm Is Not Recognized
Artinya Node.js atau npm belum terpasang dengan benar, atau PATH belum diperbarui.
Periksa:
node -v
npm -v
Tutup dan buka kembali terminal setelah instalasi.
Error: Package Does Not Exist
Pada Spring Boot, error ini biasanya disebabkan oleh:
- Dependency belum diunduh.
- Import salah.
- Package Java tidak sesuai.
- Project Maven belum selesai di-load.
Coba lakukan Maven Reload melalui IntelliJ.
Error: Cannot Resolve Symbol
Periksa nama class, package, dan import.
Contohnya:
import com.belajar.backend.model.Product;
Harus sesuai dengan lokasi class Product.
Error: Database Is Locked
H2 berbasis file umumnya tidak dapat dibuka oleh dua proses yang mencoba mengakses file database yang sama secara tidak kompatibel.
Pastikan tidak ada aplikasi lain yang membuka database tersebut secara terpisah.
Untuk tutorial ini, gunakan H2 Console yang disediakan oleh Spring Boot ketika aplikasi sedang berjalan.
Error: Halaman React Kosong
Buka Developer Tools dan periksa tab Console.
Perhatikan error JavaScript atau JSX.
Kesalahan kecil seperti kurung yang tidak ditutup dapat menyebabkan komponen gagal ditampilkan.
BAGIAN X — MENINGKATKAN PEMAHAMAN
39. Mengapa React Tidak Langsung Mengakses Database?
Pada arsitektur web modern, browser sebaiknya tidak memiliki akses langsung ke database.
Ada beberapa alasan:
Keamanan
Kredensial database tidak boleh disimpan pada kode frontend yang dapat dibaca pengguna.
Validasi
Backend harus memeriksa apakah data yang dikirim memenuhi aturan aplikasi.
Aturan Bisnis
Misalnya harga produk harus lebih besar dari nol, atau hanya pengguna tertentu yang boleh menghapus produk.
Pemeliharaan
Frontend dapat berubah tanpa harus mengubah seluruh mekanisme penyimpanan data.
Karena itu, komunikasi dilakukan melalui REST API.
40. Apa Perbedaan Spring Boot dan React?
Keduanya tidak saling menggantikan.
Spring Boot berfokus pada backend, sedangkan React berfokus pada antarmuka pengguna.
Contohnya:
| Kebutuhan | Teknologi |
|---|---|
| Menampilkan form | React |
| Menampilkan tabel | React |
| Menangani klik tombol | React |
| Memvalidasi request di server | Spring Boot |
| Menjalankan logika bisnis | Spring Boot |
| Menyimpan data | Spring Data JPA |
| Mengelola database | H2 |
Validasi pada React membantu pengalaman pengguna, tetapi validasi backend tetap diperlukan karena API dapat dipanggil tanpa melalui React.
41. Apakah Harus Menggunakan Dua Project?
Tidak selalu.
Untuk pembelajaran, kita sengaja memisahkan frontend dan backend.
Strukturnya:
belajar-spring-react/
├── backend/
└── frontend/
Pendekatan ini memiliki keuntungan:
- Lebih mudah memahami tanggung jawab setiap aplikasi.
- Frontend dapat dikembangkan secara terpisah.
- Backend dapat digunakan oleh aplikasi lain.
- Lebih mudah mempelajari REST API.
Pada tahap deployment, hasil build React dapat dilayani oleh web server terpisah atau dikemas bersama aplikasi backend, tergantung kebutuhan arsitektur.
BAGIAN XI — LANGKAH SELANJUTNYA
42. Mengganti H2 dengan PostgreSQL
Setelah memahami CRUD, langkah berikutnya adalah menggunakan database yang lebih umum untuk aplikasi production, seperti PostgreSQL.
Secara konsep, kita tidak perlu menulis ulang seluruh aplikasi.
Entity, Service, Controller, dan React tetap dapat digunakan.
Perubahan utama meliputi:
- Menambahkan dependency PostgreSQL JDBC Driver.
- Mengubah konfigurasi datasource.
- Membuat database PostgreSQL.
- Mengatur kredensial secara aman.
- Mengelola migrasi database.
Ini menunjukkan manfaat penggunaan Spring Data JPA: aplikasi tidak harus bergantung langsung pada SQL database tertentu untuk operasi sederhana.
43. Memecah Komponen React
Dalam tutorial ini, seluruh kode frontend berada di App.jsx.
Untuk aplikasi yang lebih besar, struktur dapat diubah menjadi:
src/
├── components/
│ ├── ProductForm.jsx
│ └── ProductList.jsx
├── services/
│ └── productService.js
├── App.jsx
├── App.css
└── main.jsx
ProductForm menangani form produk.
ProductList menangani tabel.
productService.js menangani komunikasi API.
Pemisahan ini akan membuat kode lebih mudah dipelihara.
44. Fitur Tambahan untuk Latihan
Setelah aplikasi CRUD berhasil, cobalah beberapa pengembangan berikut:
- Tambahkan field kategori produk.
- Tambahkan field stok.
- Tambahkan pencarian berdasarkan nama.
- Tambahkan sorting berdasarkan harga.
- Tambahkan pagination.
- Tambahkan validasi nama produk maksimal 100 karakter.
- Tambahkan halaman detail produk.
- Tambahkan notifikasi yang lebih menarik.
- Tambahkan autentikasi pengguna.
- Ganti H2 dengan PostgreSQL.
Mulailah dari satu fitur kecil. Jangan langsung mencoba menambahkan semuanya sekaligus.
BAGIAN XII — CATATAN PENTING SEBELUM PRODUCTION
Aplikasi yang kita buat sudah cukup untuk memahami CRUD, tetapi belum dirancang sebagai aplikasi production.
Beberapa hal yang perlu ditingkatkan adalah:
Keamanan
Tambahkan autentikasi dan otorisasi, misalnya menggunakan Spring Security.
Validasi
Pisahkan request DTO dari Entity agar input API lebih terkontrol.
Error Handling
Gunakan mekanisme penanganan exception yang konsisten agar frontend menerima pesan kesalahan yang jelas.
Database Migration
Gunakan Flyway atau Liquibase untuk mengelola perubahan struktur database.
Konfigurasi
Jangan menyimpan password atau kredensial production langsung di source code.
Testing
Tambahkan unit test dan integration test.
Ketepatan Nilai Uang
Backend sudah menggunakan BigDecimal, tetapi contoh React mengubah harga menjadi JavaScript Number demi kesederhanaan. Untuk aplikasi keuangan yang membutuhkan presisi desimal tinggi, gunakan representasi string atau satuan integer terkecil dengan aturan pembulatan yang jelas.
Optimasi React
Contoh useEffect dalam tutorial cukup untuk latihan, tetapi aplikasi yang lebih kompleks sebaiknya mengelola pembatalan request, loading state, dan kemungkinan respons asynchronous yang datang tidak berurutan.
Kesimpulan
Membangun aplikasi web menggunakan Spring Boot dan React JS sebenarnya dapat dipelajari secara bertahap.
Kuncinya bukan menghafal seluruh annotation Spring atau seluruh fitur React, melainkan memahami bagaimana setiap komponen bekerja dan saling berkomunikasi.
Dari tutorial ini, kita telah mempelajari:
- Menyiapkan lingkungan Java dan JavaScript.
- Membuat project Spring Boot menggunakan Maven.
- Memahami Entity, Repository, Service, dan Controller.
- Membuat REST API CRUD.
- Menyimpan data menggunakan H2 dan Spring Data JPA.
- Membuat frontend menggunakan React dan Vite.
- Memahami state, Effect, JSX, dan HTTP request.
- Menghubungkan React dengan Spring Boot.
- Menguji dan melakukan debugging aplikasi.
Hal terpenting yang perlu dipahami adalah bahwa React bertanggung jawab terhadap pengalaman pengguna, Spring Boot menangani proses aplikasi, dan database bertugas menyimpan data.
Ketika hubungan ketiganya sudah dipahami, pengembangan aplikasi yang lebih kompleks akan terasa jauh lebih mudah.
CRUD mungkin terlihat sederhana, tetapi konsep di baliknya merupakan fondasi dari banyak aplikasi bisnis, mulai dari sistem inventori, aplikasi keuangan, sistem ticketing, hingga platform enterprise.
Belajar software development bukan tentang seberapa banyak framework yang kita kuasai, tetapi seberapa baik kita memahami cara sebuah sistem bekerja.
Referensi
- Spring Boot — https://spring.io/projects/spring-boot
- Spring Initializr — https://start.spring.io/
- Spring Data JPA — https://spring.io/projects/spring-data-jpa
- React Documentation — https://react.dev/
- Vite Documentation — https://vite.dev/
- Java Documentation — https://dev.java/
- Node.js — https://nodejs.org/
- IntelliJ IDEA — https://www.jetbrains.com/idea/
- Visual Studio Code — https://code.visualstudio.com/
Ditulis untuk pembaca Nirantara yang ingin memulai perjalanan pengembangan aplikasi full stack, dari memahami konsep hingga membangun aplikasi yang benar-benar dapat dijalankan.
Published by Nirantara
Nirantara is an Indonesian Enterprise AI company specializing in Enterprise AI, RAG, Intelligent Search, and AI Automation. Article written by jsturana.