A BLOG POST

Advisory Program 2022 (Chapter 1) : Create Your First API

Tulisan ini adalah bagian dari serangkaian materi dari Advisory Program BCA, untuk source code nya bisa dilihat di sini.


Yo! Fellowdevs, welcome to the program! Pada program ini, kita akan belajar mengenai bagaimana membuat sebuah aplikasi todo sederhana berbasis web yang terdiri dari backend dan frontend dengan menggunakan Spring Boot dan Angular. So, tanpa menunda-nunda lagi, let's start creating our API πŸ’¨

Advisory Program 2022 (Chapter 1) : Create Your First API image
chandrawijaya image
chandrawijayaPublished at Β  3/4/2022, 7:05:00 AM

Updated atΒ 10/20/2024, 3:25:17 AM

Read Β 952 times

Sebelum memulai masuk lebih dalam, ada beberapa program yang perlu kita install terlebih dahulu, di antaranya:

  • Java Development Kit (JDK) versi 11 or later (link)
  • Maven (how to)
  • IDE (sesuai preferensi)
  • PostgreSQL, saya menggunakan versi 12 (link)
  • Postman, opsional (link)

Menjelaskan instalasi masing-masing program tersebut akan memakan waktu dan membuat tulisan ini menjadi lebih panjang dan melebar, so saya harap fellowdevs bisa mencari referensi di internet ya, banyak sekali kok 😁

Perlu diketahui bahwa pada program ini saya tidak menjelaskan mengenai tutorial, misalnya tutorial Java, tutorial Spring atau tutorial Angular. Namun apa yang saya jelaskan lebih ke bagaimana implementasi framework tertentu pada aplikasi sederhana dan integrasinya berdasarkan konsep yang dibahas, seperti misalnya model arsitektur, mengapa menggunakan stack tertentu, desain struktur program atau do's and don'ts.


Hal ini saya tujukan karena materi seperti tutorial-tutorial tersebut sangat mudah fellowdevs dapatkan melalui platform online course seperti Udemy atau bahkan video gratis seperti Youtube. Nyatanya, saya sendiri pun belajar banyak dari platform-platform tersebut. Akan sangat memakan waktu dan tenaga apabila saya berusaha membuat tutorial serupa yang berulang dimana saya sendiri tidak begitu yakin bahwa content yang saya deliver akan lebih baik dari para creator tersebut.


Ditambah lagi, saya ingin berfokus pada hal-hal lain yang lebih diperlukan pada dunia industri profesional. Misalnya seperti desain arsitektur aplikasi, security, common practices, tips & trik, atau bahkan sekedar memperkenalkan tools atau teknologi yang mungkin belum dikenal sebelumnya namun banyak digunakan di pekerjaan nantinya.


Oleh karena itu, saya mendorong fellowdevs peserta program untuk dapat lebih dahulu mempelajari konsep dasar dari stack yang akan digunakan, misalnya seperti mengerti konsep dasar Java, OOP, Javascript, database, dll. Hal ini akan membantu fellowdevs dalam mengikuti program ini dengan lebih efektif.

Arsitektur FEBE, as I usually call it, atau juga dikenal BFF, terdiri dari paling sedikit dua bagian utama, yaitu frontend dan backend. Backend biasanya berperan sebagai perantara dalam bentuk API untuk berhubungan dengan bagian "dalam" aplikasi seperti database, authorization manager, atau bahkan aplikasi lain. Sedangkan frontend akan berperan sebagai user interface bagi pengguna agar dapat berinteraksi dengan aplikasi kita. Arsitektur ini sudah sangat common digunakan dalam membangun aplikasi terutama aplikasi berbasis web.

Tapi, kenapa kita perlu memisahkan frontend dengan backend sih?

Ada beberapa alasan kenapa kita perlu melakukan pemisahan ini. Berdasarkan pengalaman saya pribadi, dua hal terpenting untuk melakukan separasi ini adalah alasan Scalability dan Modularity. Namun, di artikel ini saya tidak akan membahas mengenai hal tersebut, kita akan bahas lagi lain waktu.

Sebagai referensi, fellowdevs dapat membaca dari sini untuk memulai.

Gambaran arsitektur aplikasi yang akan kita bangun berdasarkan pemisahan tersebut adalah sebagai berikut.

Project Architecture Overview image
Project Architecture Overview

Teknologi yang akan kita gunakan untuk membangun aplikasi sederhana kita adalah Spring Boot untuk backend, dimana kita akan menggunakan komponen-komponen seperti Spring Web MVC untuk membuat REST API, Spring Data JPA untuk berinteraksi dengan database kita.
Untuk database, kita akan menggunakan PostgreSQL saja untuk kemudahan.


Apabila kamu belum familiar dengan API, monggo untuk baca dulu artikel ini.


Nah, aplikasi yang akan kita buat adalah aplikasi to-do list sederhana dimana pengguna dapat menambahkan item ke daftar to-do dan mengubah status item to-do. Kurang lebih, API yang akan kita buat nantinya adalah berikut ini.

Method
β–²
Endpoint
β–²
Action
β–²
GET
/adam/todo
Mendapatkan daftar task
POST
/adam/todo
Menambahkan task ke dalam todo list
PUT
/adam/todo
Mengubah status dari sebuah task (digunakan untuk done / undone)

Java adalah bahasa pemrograman yang dapat dibilang "tua", dan di zaman sekarang ini banyak developer yang mungkin sudah tidak menggunakannya lagi. Berkaitan dengan hal tersebut, fellowdevs dapat membaca di artikel ini mengenai mengapa saya masih menggunakan Java hingga saat ini😁

Lalu mengapa Spring? Untuk jawabannya, fellowdevs tidak akan kesulitan mendapatkannya di internet. Namun dalam perjalanan saya bersama Java selama kurang lebih 10 tahun ini, tidak ada yang lebih mudah melakukan Java development daripada menggunakan Spring sebagai framework-nya.


Menurut saya, sepertinya di luar sana Spring sudah menjadi "de facto" nya Java framework. Memang banyak alternatif lain selain Spring, seperti Struts, JSF, Vaadin, Grails, you can name it. But with Spring, kita mendapatkan pengalaman pemrograman yang lebih mudah, lebih cepat, dan lebih aman.


Untuk lebih lengkap silakan cek link ini, Why Spring?

Untuk membuat aplikasi Spring Boot, Spring telah menyediakan sebuah generator yang memudahkan kita untuk memulai, yaitu Spring Initializr. Sebenarnya ada juga cara lain seperti membuat sebuah Maven project kemudian kita tambahkan Spring Boot dependency-nya, tapi for the sake of simplicity kita pakai Initializr saja ya ✌😁


Pada Spring Initialzr, saya akan menggunakan Maven untuk build tool-nya, jar sebagai pilihan packaging-nya, and of course Java untuk language-nya dengan Java 11 untuk versinya. Untuk versi Java sendiri fellowdevs dapat memilih versi java 8 atau bahkan 17. Namun saat blog ini saya tulis, Java 11 adalah versi menengah yang tidak terlalu tertinggal dan juga tidak terlalu baru (cenderung stabil).


Metadata projeknya dapat diisi seperti ini:

  • Group: com.bca
  • Artifact: adam
  • Name, Description, Package Name: default


Nah kemudian, kita perlu untuk menambahkan dependency apa saja yang kita butuhkan dalam project kita. Kalau kalian bingung dengan istilah dependency, anggaplah dependency sebagai kumpulan code yang telah ditulis oleh developer lain yang dapat kita reuse agar kita tidak perlu membuatnya lagi. Dependency ini dapat juga kita sebut sebagai library apabila itu lebih mudah untuk fellowdevs pahami.

Untuk menambahkan dependency, fellowdevs tinggal klik saja di tombol Add Dependencies atau tekan Ctrl + B untuk shortcut, lalu tinggal ketik nama dependency yang kita ingin tambahkan.


Dependency yang akan kita gunakan adalah:

  • Spring Web (komponen Spring utama yang kita butuhkan untuk membuat aplikasi Spring Web atau REST API dengan Spring)
  • Spring Data JPA (diperlukan untuk berinteraksi dengan database tanpa harus membuat koneksi dan query secara manual)
  • Lombok (sebuah library yang sangat berguna untuk memudahkan kita dalam menuliskan program Java, terutama untuk mempersingkat dan mengurangi redundansi yang tidak perlu)
  • PostgreSQL Driver (driver yang diperlukan untuk koneksi ke database kita yaitu Postgres)
  • Spring Boot Devtools (library ini akan memudahkan kita dalam proses development terutama untuk hot reload)


And that's it! Just to sum it up, berikut adalah konfigurasi Spring Initialzr kita.

Spring Initializr image
Spring Initializr


Now all we need to do is click Generate atau Ctrl + ⏎ yang kemudian browser kita akan secara otomatis men-download project tersebut.

Okay, setelah project dari Spring Initializr berhasil kita download ke local machine kita, mari kita extract (it is a .zip file) dan load project-nya ke IDE kita.

Talking about IDE, bagi saya, IDE ibarat agama, jadi saya tidak mengharuskan fellowdevs untuk mengikuti dengan IDE yang saya gunakan. But in my development, most of the time I will go with VSCode. Apabila fellowdevs tidak punya preferensi IDE dan ingin menggunakan VSCode juga, silakan kalian dapat download dan install dulu di sini.

Untuk membuka project Spring kita ke IDE ada banyak cara, namun secara umum, cari opsi seperti Open Project atau Open Folder pada IDE yang fellowdevs gunakan, lalu arahkan ke folder project hasil extract tadi.

Untuk VSCode sendiri, fellowdevs dapat menggunakan cara yang lebih singkat dengan klik kanan pada folder project kita dan pilih menu Open with Code.

image-e64284262d712882fc5387b33af32791e8d051bb-890x528-png image

Nah, sebelum kita melangkah lebih dalam, ada baiknya kita lebih dulu mengerti apa saja file-file yang ada di dalam project kita, terutama bagi fellowdevs yang belum familiar dengan Spring framework.

1.
2β”œβ”€β”€ .mvn
3β”œβ”€β”€ src
4β”‚   β”œβ”€β”€ main
5β”‚   β”‚   β”œβ”€β”€ java
6β”‚   β”‚   β”‚   └── com/bca/adam
7β”‚   β”‚   β”‚       β”œβ”€β”€ AdamApplication.java
8β”‚   β”‚   β”‚       └── ServletInitializer.java
9β”‚   β”‚   └── resources
10β”‚   β”‚       β”œβ”€β”€ static
11β”‚   β”‚       β”œβ”€β”€ templates
12β”‚   β”‚       └── application.properties
13β”‚   └── test
14β”œβ”€β”€ .gitignore
15β”œβ”€β”€ HELP.md
16β”œβ”€β”€ mvnw
17β”œβ”€β”€ mvnw.cmd
18└── pom.xml

Most of the time, kita akan bekerja pada folder /src/. Di luar folder ini, satu-satunya file yang mungkin akan kita ubah adalah filel pom.xml. Itu pun kalau memang ada dependency baru atau ada konfigurasi Maven khusus yang perlu dilakukan.

Di dalam folder /src/, terdapat dua folder lain yaitu /main dan /test. Folder /test ini tidak akan saya bahas dulu karena akan menambah kompleksitas project, basically folder ini akan menampung semua file hasil generate saat proses automated testing oleh Maven. Dan karena kita tidak menggunakannya, kita hapus saja.

Kemudian di dalam folder /main, akan ada dua folder lagi yaitu /java dan /resources. Folder /resources akan menampung file-file pendukung yang akan digunakan oleh aplikasi, misalnya seperti file konfigurasi, assets, templates, etc. Nah, ada satu file di dalam folder /resources yang juga disediakan untuk kita, yaitu application.properties. File ini biasanya kita gunakan untuk konfigurasi yang akan dibaca oleh Spring saat proses bootup. File ini tidak mandatory namun biasanya akan digunakan sekalipun dalam aplikasi sederhana. FYI, file ini dapat berupa .yml juga ya.


Selanjutnya kita akan masuk ke dalam main course-nya, yaitu di dalam folder /java. Kita akan menemui subfolders yang sesuai dengan packaging yang kita tentukan di Initializr, yaitu /com/bca/adam/. Di dalam folder inilah kita akan fokus untuk development kita.

Di dalam folder /adam/, ada dua file .java yang sudah di-generate oleh Spring. Apakah fungsi dua file ini? Apakah kita bisa modifikasi?

Nah, hal ini berkaitan dengan bagaimana sebuah aplikasi Spring berjalan. Pada saat proses bootup dimulai (kita akan jelaskan nanti), most frameworks akan melakukan apa yang disebut dengan bootstraping. Sederhananya, proses ini berjalan secara internal dimana aplikasi akan mulai membaca semua konfigurasi yang diperlukan atau yang telah di-define oleh developer, kemudian me-load konfigurasi tersebut ke dalam sistem sehingga dapat digunakan untuk memulai. Thanks to Spring Boot Starter, developer tidak perlu pusing mengenai bagaimana proses bootstraping oleh Spring Boot berjalan.


Loh kapan kita mendefinisikan Spring Boot Starter ini? Kita kan belum menyentuh apapun dari project ini? Nah, kalau kita lihat di dalam file pom.xml, kita akan menemukan dependency spring-boot-starter-web pada segment dependencies. Dependency inilah yang memungkinkan developer tidak perlu melakukan banyak konfigurasi manual pada aplikasi Spring Boot. Hal ini juga berlaku pada dependency spring-boot-starter-jpa yang juga kita define, dependency ini akan memudahkan kita dalam melakukan transaksi dengan database.

xml

1<dependencies>
2	<dependency>
3		<groupId>org.springframework.boot</groupId>
4		<artifactId>spring-boot-starter-data-jpa</artifactId>
5	</dependency>
6	<dependency>
7		<groupId>org.springframework.boot</groupId>
8		<artifactId>spring-boot-starter-web</artifactId>
9	</dependency>
10	...
11</dependencies>

Spring Boot akan memulai prosesnya melalui class yang diberikan anotasi @SpringBootApplication. Inilah entry point setiap aplikasi Spring Boot. Di dalam file AdamApplication.java, fellowdevs dapat melihat sendiri bahwa di dalam class tersebut sudah dianotasikan demikian oleh Spring Initializr, sehingga aplikasi kita akan berjalan melalui class ini.

Kemudian, anotasi @ComponentScan digunakan untuk memberi tahu Spring bahwa Spring perlu melakukan scanning terhadap components yang terdapat pada direktori / path yang disebutkan dalam parameter. Apabila tidak ada anotasi ini, Spring tidak akan tahu lokasi component-component yang kita buat dan sehingga component tersebut tidak dapat digunakan. Dalam hal ini, kita akan menggunakan component-component di dalam package com.bca.adam.

Pada konsep Java, untuk kita dapat run aplikasi, kita memerlukan method yang disebut sebagai main method. Di class AdamApplication.java, main method juga telah dituliskan dan didalamnya terdapat fungsi SpringApplication.run(primarySource, args) dimana method ini akan menginstrusikan Spring app untuk berjalan.

java

1@SpringBootApplication
2public class AdamApplication {
3
4	public static void main(String[] args) {
5		SpringApplication.run(AdamApplication.class, args);
6	}
7
8}


Kembali ke pertanyaan awal, sekarang kita sudah tahu fungsi dari file AdamApplication.java, kemudian apa fungsi dari file ServletInitializer.java? Sebenarnya, pada project sederhana kita kali ini, kita belum menggunakannya. Namun secara singkat, file ini adalah konfigurasi yang akan kita butuhkan apabila kita melakukan deployment aplikasi kita dengan menggunakan servlet container/web server, seperti JBoss.

Biasanya kita menginginkan cara run tradisional seperti WAR archive. Namun karena kita tidak melakukan deployment tersebut, sebenarnya kita hapus pun tidak masalah. Sekedar informasi, bila kita hendak melakukan deployment dengan servlet container, maka kita memerlukan konfigurasi dengan meng-extend SpringBootServletInitializer. Additional information, by default Spring Boot hadir dengan embedded servlet container yakni Tomcat yang runnable with zero configuration dan inilah yang akan kita gunakan.


Nah setelah mengetahui bahwa kedua file .java yang ter-generate ini adalah untuk konfigurasi aplikasi Spring Boot kita, apakah file-file ini bisa diubah? Tentu bisa, karena ini adalah konfigurasional, tentu saja at some points fellowdevs akan menemui waktu dimana diperlukan untuk modifikasi konfigurasi bawaan ini.

Salah satu kelebihan menggunakan Spring Boot adalah konfigurasinya yang otomatis tersedia bahkan tanpa perlu menambahkan konfigurasi khusus kecuali memang kita membutuhkannya. Hal ini juga berlaku untuk pembuatan backend project yang akan kita kerjakan ini. Kalau dibandingkan dengan Spring framework-nya saja, Spring Boot sudah sangat membantu jauh dalam banyak aspek, apalagi kalau dibandingkan dengan native Java project. Saya sih ogah πŸ˜‚.
Sebelum kita masuk ke development yang kompleks, yuk kita coba membuat sebuah simple API (Hello World API) untuk membuktikan hal tersebut.


Buat sebuah folder /package di dalam package com.bca.adam dengan nama controller. Penamaan dan struktur folder ini sebenarnya tidak mandatory, hanya saja akan lebih rapi dan bersih kalau kita klasifikasikan komponen-komponen project kita ke dalam package yang sesuai. Lalu di dalam controller, Java class dengan nama HelloWorldController.java. Kembali saya terangkan bahwa penamaan ini tidaklah mandatory, suffix Controller pada Java class kita akan membantu kita nantinya terutama apabila project kita menjadi lebih besar dan kompleks.


Class HelloWorldController.java akan menjadi controller untuk API Hello World kita. Controller sendiri dapat fellowdevs anggap sebagai route pada URL kita nantinya. Gambaran sederhananya seperti ini.

How Spring's routing system works image
How Spring's routing system works

Ada banyak kombinasi lain yang dapat fellowdevs temukan di dokumentasi Spring official.


Sekarang, kita perlu memberi tahu Spring bahwa class ini adalah sebuah controller. Bagaimana caranya? Tentu sangatlah mudah. Spring menggunakan banyak abstraksi dalam bentuk anotasi (@....) untuk menentukan jenis komponen sebuah object. Hal ini disebut juga sebagai stereotype. Untuk membuat REST API, kita akan gunakan anotasi @RestController yang akan kita arahkan ke route /hello untuk memisahkan dari controller utama kita nanti.

Tambahkan anotasi @RestController ke controller kita ini. Lalu kita juga perlu menambahkan anotasi @RequestMapping untuk menentukan route ke API kita. Singkatnya, anotasi ini kita gunakan dalam menentukan routing ke API yang kita buat. Baca juga artikel ini untuk penjelasan yang lebih mendetil. Route yang akan kita gunakan adalah /hello, maka kita tambahkan parameter "hello" di dalam anotasi @RequestMapping.

java

1package com.bca.adam.controller;
2
3import org.springframework.web.bind.annotation.RestController;
4import org.springframework.web.bind.annotation.RequestMapping;
5
6@RestController
7@RequestMapping("hello")
8public class HelloWorldController {
9
10}

Jangan lupa ya pastikan IDE kalian menambahkan import statement nya secara otomatis. Apabila tidak, maka kalian perlu menambahkan import tersebut secara manual atau dapat melakukan konfigurasi ulang untuk dapat support Java dan Spring seperti ini.


Sampai tahap ini, kita sudah memiliki entry point ke controller kita yaitu /hello. Namun, pada pemanggilan REST API kita pasti akan menentukan HTTP Method apa yang digunakan, misalnya GET, POST, PUT, atau DELETE, serta mungkin kita memerlukan nested routes. Nah, hal ini kita lakukan melalui functions pada controller kita dengan dianotasikan dengan anotasi seperti @RequestMapping, @GetMapping, @PostMapping, dan lain sebagainya. Pada contoh ini misalnya, kita akan membuat sebuah GET API, yang akan diarahkan ke route /hello. Untuk membuatnya, kita cukup menambahkan:

java

1@RestController
2@RequestMapping("hello")
3public class HelloWorldController {
4
5    @GetMapping
6    public String index() {
7        return "Hello World!";
8    }
9
10}

Karena route yang ingin kita arahkan untuk sample API ini tidak memiliki nested routes, maka kita tidak perlu menambahkan parameter apapun pada anotasi @GetMapping. Akan lain halnya apabila fellowdevs ingin membuat API dengan nested route misalnya /hello/hi. Untuk ini, kita perlu memberikan value "hi" pada anotasi @GetMapping kita, misalnya:

java

1@RestController
2@RequestMapping("hello")
3public class HelloWorldController {
4
5    @GetMapping
6    public String index() {
7        return "Hello World!";
8    }
9
10    @GetMapping("hi")
11    public String hi() {
12        return "Hi World!";
13    }
14}

Nah, mengenai anotasi seperti @GetMapping, @PostMapping, etc yang saya sebut-sebut ini sama cara kerjanya dengan @RequestMapping, hanya saja anotasi ini adalah shortcuts. Kalian dapat mempelajarinya lebih detil di sini.


Tanpa fellowdevs sadari, dengan mengikuti langkah-langkah tadi, kita sudah baru saja membuat dua API sederhana yang dapat digunakan. Untuk menggunakan API tersebut, mari kita run dulu aplikasi Spring Boot kita. Cara termudah untuk run project Spring Boot adalah dengan menggunakan tool seperti Maven atau Gradle. Ya, pada dasarnya memang Maven atau Gradle sudah menjadi common tool juga dalam development Java.

Pada project ini kita menggunakan Maven, maka langkah yang kita perlu lakukan untuk run project ini adalah:

  • Buka terminal pada IDE kalian masing-masing, pada VSCode bisa dengan menggunakan shortcut Ctrl + ~. Atau bisa juga kita menggunakan CMD atau command prompt apabila fellowdevs lebih terbiasa di Windows.
  • Pastikan lokasi folder di terminal saat ini adalah di dalam project kita.
  • Run command mvn clean spring-boot:run
  • Berdoa agar tidak error πŸ˜‚

Here is a screenshot dari IDE saya untuk referensi.

Running Spring Boot project image
Running Spring Boot project

Setelah menjalankan command tadi, tunggu beberapa saat untuk Spring akan memulai keajaibannya. Jangan kaget kalau akan banyak yang tiba-tiba muncul di terminal kita, itu semua adalah log yang dituliskan oleh Spring, ini akan sangat membantu kita untuk mengetahui jika ada yang salah/error. Kalau segala sesuatunya lancar, maka fellowdevs akan mendapatkan informasi seperti ini di log console masing-masing (abaikan apabila ada log lain yang berbeda dengan kalian).

image-abfe8504e4472d72a80b0fc31a635d07fc47392e-1592x535-png image

Dari log tersebut, kita dapat mengerti bahwa aplikasi kita sudah berhasil up di atas Tomcat (embedded servlet container-nya) di port 8080 dengan context root/context path /adam. Context root ini adalah entry point untuk masuk ke aplikasi kita, berbeda dengan anotasi @RestController atau @Controller yang merupakan entry point ke API yang kita buat.


Nah, sekarang saatnya kita coba API yang kita buat. Cara paling mudah untuk melakukannya adalah dengan menggunakan browser. Since kedua API kita ini menggunakan method GET, maka tidak jadi masalah dengan menggunakan browser. Namun untuk method lain seperti POST misalnya, kita akan membutuhkan tool lain yang memudahkan kita melakukan hit ke API kita tersebut seperti cURL atau Postman.


Melalui browser, ketik pada address bar http://localhost:8080/adam dan enter. Apa yang dapat kita lihat? Sebuah error page!

Response of /adam : 404 Not Found image
Response of /adam : 404 Not Found

βœ‹ Eits! Jangan putus asa dulu. Kenapa kita dihadapkan dengan error page ini? Informasi yang penting yang bisa kita peroleh melalui halaman ini ada pada baris terakhir, yaitu Not Found atau 404. Kode ini adalah HTTP Status Code dimana kode error yang diawali dengan angka adalah 4 adalah error yang disebabkan oleh client atau dalam hal ini adalah kita yang memanggil. Kode 404 sendiri berarti kita melakukan request terhadap resource yang tidak ada atau tidak ditemukan.

Perhatikan URL yang kita hit, yaitu /adam, seperti yang saya mention sebelumnya ini adalah context path/context root aplikasi kita, bukan ke controller atau API entry points. Nah, kalau kita lihat lagi ke code kita, kita tidak pernah mendefinisikan API dengan route /adam bukan? Satu-satunya controller kita yaitu HelloWorldController.java, kita mapped ke /hello. Artinya, kita memang tidak punya controller yang meng-handle route ke /adam, melainkan /adam/hello.


Dengan demikian, kita perlu mengarahkan request kita ke /adam/hello instead of /adam. And voila!

Response of /adam/hello image
Response of /adam/hello

Kita sudah berhasil mendapatkan response dari API yang kita buat. Saya harap fellowdevs sudah mulai mengerti bagaimana konsep routing-nya yang terjadi melalui anotasi @RequestMapping. Request /adam/hello akan diarahkan ke controller /hello di HelloWorldController.java yakni fungsi index(). Sedangkan untuk mengarahkan ke API di fungsi hi(), maka kita cukup arahkan request kita ke /adam/hello/hi.

Response of /adam/hello/hi image
Response of /adam/hello/hi
Perlu diperhatikan bahwa path /adam yang saya tambahkan adalah effect dari property server.servlet.context-path yang saya tambahkan di application.properties pada contoh project yang saya sediakan. Apabila fellowdevs tidak menggunakan contoh project saya, maka path /adam ini dapat dihilangkan karena by default Spring tidak menambahkan context root apapun ke dalam aplikasi.


Selamat! Karena kalian baru saja berhasil membuat dua API dengan menggunakan Spring Boot! Cukup mudah bukan? Nah, kita akan lanjut dengan case study kita yaitu aplikasi to-do list dengan tiga API yang akan kita buat.

Untuk menghentikan aplikasi, simply close terminal atau ketik CTRL + C pada terminal.

Project aplikasi kita akan menggunakan real data yang tersimpan di dalam database PostgreSQL, untuk itu kita perlu melakukan setup database kita terlebih dahulu.

Ada beberapa cara untuk membuat database PostgreSQL. Apabila saat instalasi PostgreSQL fellowdevs memilih untuk meng-install juga pgAdmin (aplikasi GUI PostgreSQL) maka kalian dapat menggunakannya untuk membuat database baru. Caranya pun mudah karena sudah tersedia menu-menu yang dapat dipahami.


Namun saya akan menggunakan CLI karena kemungkinan tidak semua fellowdevs pembaca tulisan ini meng-include instalasi pgAdmin.

Untuk membuat database PostgreSQL dengan CLI, kita akan menggunakan terminal atau CMD. Buka CMD dan masuk ke dalam mode psql dengan mengetikkan command berikut.

sh

1psql -U postgres

Di command tersebut saya menambahkan parameter -U untuk memilih user database saya. Default PostgreSQL akan menambahkan user postgres, apabila fellowdevs hendak menggunakan user kalian masing-masing silakan ganti dengan nama user kalian. Pada tahap selanjutnya, kita akan diminta untuk memasukkan password untuk user kita. Password ini kita dapatkan saat kita mendaftarkan user kita, jadi diingat sendiri ya. Untuk case user postgres, apabila saat instalasi tidak mengisi password, maka dapat dikosongkan.

Getting into PSQL mode image
Getting into PSQL mode

Setelah kita berhasil login, kita akan berada dalam mode psql dengan ditandai prefix CLI kita adalah postgres=#. Untuk membuat database, kita bisa menggunakan command berikut.

sh

1CREATE DATABASE bcaitworks;

Setelah menjalankan command tersebut, kita dapat mengecek apakah database tersebut sudah sukses terbentuk dengan menggunakan command \l dan muncul list database kita.

image-8646071ee2bd5f78ee4a13026a28b67805195e6b-1021x267-png image

Bisa dilihat bahwa database bcaitworks kita sudah terbentuk. Nah biasanya setelah kita membuat database tahap selanjutnya adalah membuat komponen di dalamnya, seperti schema, tables, relations dll.

Tapi saya ingin menunjukan kepada fellowdevs keuntungan menggunakan teknologi JPA (tidak hanya spesifik Spring) yang dapat membantu kita dalam membuat komponen tersebut secara otomatis hanya dengan membuat entity class kita. Entity class ini sering juga disebut sebagai model.

Gampangnya entity/model ini adalah representasi tabel-tabel database kita dalam bentuk class atau POJO sehingga kita dapat menggunakannya dengan mudah sebagai object di dalam program kita.

Sebuah task dalam to-do list, biasanya akan memiliki judul sebagai nama dari task tersebut, serta status mengenai task tersebut apakah sudah selesai/belum. Untuk itu, tabel yang akan menampung data to-do kita perlu ditambahkan fields/kolom berikut ini.

Todo table overview image
Todo table overview
  • id adalah kolom identifier untuk setiap record. Nantinya kita akan isi dengan UUID.
  • Kolom title akan menampung judul dari task.
  • is_done akan menampung status dari task.
  • Empat kolom lain yaitu created_by, modified_by, created_date dan modified_date adalah kolom audit trail. Saya sangat merekomendasikan untuk selalu menambahkan kolom audit trail semacam ini dalam setiap tabel terutama tabel yang ditujukan untuk menyimpan data transaksi aplikasi. Audit trail fields ini akan membantu kita nantinya terutama dalam hal data integrity.
Field created_by dan modified_by akan kita gunakan pada Chapter 3.

Nah, kita sudah selesai pada tahap database design/modeling. Selanjutnya, kita akan langsung membuat tabel ini melalui entity class di project kita.

Lanjut di IDE kita, mari buat folder baru untuk menampung entity class kita. Saya akan namakan folder ini dengan nama model. Di dalamnya, kita buat class dengan nama Todo.java. Sekali lagi, class ini akan menggambarkan tabel todo kita di dalam database.


Pada class ini, kita akan membuat fields tadi melalui variables yang ditambahkan anotasi yang menunjukan stereotype dari object tersebut.

java

1package com.bca.adam.model;
2
3import java.util.Calendar;
4import java.util.Date;
5import java.util.UUID;
6
7import javax.persistence.Column;
8import javax.persistence.Entity;
9import javax.persistence.GeneratedValue;
10import javax.persistence.Id;
11import javax.persistence.Table;
12
13import org.hibernate.annotations.ColumnDefault;
14import org.hibernate.annotations.GenericGenerator;
15import org.hibernate.annotations.Type;
16
17import lombok.Data;
18import lombok.EqualsAndHashCode;
19import lombok.ToString;
20
21@Entity
22@Table(name = "todo")
23@Data
24@ToString
25@EqualsAndHashCode
26public class Todo {
27
28    @Id
29    @GeneratedValue(generator = "UUID")
30    @Type(type = "pg-uuid")
31    @GenericGenerator(name = "UUID", strategy = "org.hibernate.id.UUIDGenerator")
32    @Column(name = "id", updatable = false, nullable = false)
33    private UUID id;
34
35    @Column(name = "title")
36    private String title;
37
38    @Column(name = "created_date")
39    @ColumnDefault("NOW()")
40    private Date createdDate;
41
42    @Column(name = "modified_date")
43    private Date modifiedDate;
44
45    @Column(name = "is_done")
46    private boolean isDone;
47
48    public Todo(String title) {
49        this.title = title;
50        this.createdDate = Calendar.getInstance().getTime();
51    }
52}

Penjelasan dari code di atas:

  • @Entity adalah anotasi untuk memberi tahu Spring bahwa class ini adalah sebuah entity object. Dalam JPA, entity adalah wajib.
  • @Table menerangkan bahwa class ini adalah representasi dengan tabel di database dengan nama todo.
  • @Data, @ToString, @EqualsAndHasCode adalah anotasi dari Lombok. Library ini memudahkan kita terutama dalam membuat aksesor mutator sebuah object. Selebihnya tentang Lombok dapat dipelajari di sini.
  • @Id adalah anotasi untuk menunjukan ID atau dapat juga dianalogikan sebagai primary key (PK) pada entity. Entity wajib memiliki ID.
  • Karena ID ini akan kita generate dengan format UUID, maka kita perlu men-define generatornya. Dalam hal ini, kita anotasikan dengan @GeneratedValue, @Type serta @GenericGenerator seperti pada code di atas.
  • Sama halnya seperti @Table, @Column mendefinisikan bahwa field Java object ini akan menjadi field / kolom pada tabel di database.
  • @ColumnDefault digunakan untuk mendefinisikan default value suatu kolom.

Sejauh ini kita sudah membuat database untuk digunakan, serta men-design struktur database dan tabel kita. Namun, kita belum menghubungkan project kita dengan database yang kita buat tadi. Di sinilah akan kita gunakan file application.properties pada folder /resources. Seperti yang saya sampaikan di awal, file ini biasanya menampung konfigurasi dari aplikasi kita. Koneksi database termasuk konfigurasi sehingga kita dapat letakan di dalam file ini.


Untuk menambahkan konfigurasi koneksi database, kita dapat menggunakan property berikut ini.

1spring.datasource.url= jdbc:postgresql://localhost:5432/bcaitworks
2spring.datasource.username= postgres
3spring.datasource.password= admin
4
5spring.jpa.properties.hibernate.dialect= org.hibernate.dialect.PostgreSQLDialect
6
7# Hibernate ddl auto (create, create-drop, validate, update)
8spring.jpa.hibernate.ddl-auto=update
9spring.jpa.show-sql=true
  • Yang pertama kita perlu definisikan URL database kita dalam ekspresi datasource. Karena kita bekerja di localhost dan default port PostgreSQL adalah 5432, maka koneksi host kita adalah localhost:5432/{database_name}. {database_name} diganti dengan nama database masing-masing.
  • Kemudian, kita juga definisikan credentials untuk koneksi database tersebut.
  • Karena banyak varian database yang ada dan library Spring JPA dibuat se-generic mungkin, maka kita perlu secara spesifik mendefinisikan jenis database yang kita pakai. Hal ini akan berpengaruh pada dialect / query generation yang digunakan saat melakukan operasi ke database kita sesuai dengan varian yang digunakan. Dalam hal ini kita gunakan dialect dari PostgreSQL.
  • Nah untuk memanfaatkan fitur auto generation object database melalui aplikasi kita, kita perlu menambahkan property spring.jpa.hibernate.ddl-auto. Property ini memberi tahu Spring bahwa kita ingin Spring JPA untuk membuatkan object database secara otomatis saat aplikasi up pertama kali. Untuk valuenya, kita isi dengan update saja.
spring.jpa.hibernate.ddl-auto dapat juga diisi dengan create, create-drop, atau validate. create akan melakukan auto create object database apabila belum ada. create-drop akan melakukan drop keseluruhan database terlebih dahulu, lalu men-generate ulang (fresh). update akan melakukan modifikasi terhadap object database sesuai dengan perubahan yang dilakukan pada entity.
⚠️ Saya sangat merekomendasikan untuk tidak mengaktifkan fitur ini pada environment production atau untuk dev purpose saja dikarenakan auto generation ini dapat berakibat fatal bila tidak dimengerti dengan baik. Bayangkan apabila database kita sudah digunakan dan berisi data-data aplikasi yang penting, kemudian tanpa sengaja kita men-deploy ulang aplikasi dengan posisi fitur ini dinyalakan dan valuenya create-drop 🀯.
  • spring.jpa.show-sql adalah opsi apabila kita ingin menampilkan query yang di-generate oleh JPA saat aplikasi dijalankan. Karena ini akan berpengaruh pada logging system aplikasi dan sangat verbose, aktifkan hanya untuk dev purpose saja.

Setelah selesai dengan konfigurasi tersebut, maka saatnya untuk kita menjalankan aplikasi kita lagi agar dapat men-generate object database kita secara otomatis. Untuk menjalankannya dapat kita run kembali command mvn clean spring-boot:run.


Dan apabila berjalan dengan lancar, maka kita akan mendapatkan tabel kita telah dibuat secara otomatis lengkap dengan kolom dan tipe datanya. NiceπŸ‘ bukan ?

Database components are automatically created using ORM Technology image
Database components are automatically created using ORM Technology

Kita juga bisa mengecek hal ini melalui CLI tadi, yaitu dengan command berikut.

1postgres=# \c bcaitworks
2You are now connected to database "bcaitworks" as user "postgres".
3bcaitworks=# \d
4        List of relations
5 Schema | Name | Type  |  Owner
6--------+------+-------+----------
7 public | todo | table | postgres
8(1 row)
9
10
11bcaitworks=# \d todo
12                             Table "public.todo"
13    Column     |            Type             | Collation | Nullable | Default
14---------------+-----------------------------+-----------+----------+---------
15 id            | uuid                        |           | not null |
16 created_date  | timestamp without time zone |           |          | now()
17 is_done       | boolean                     |           | not null | false
18 modified_date | timestamp without time zone |           |          |
19 title         | character varying(255)      |           | not null |
20 created_by    | character varying(255)      |           |          |
21 modified_by   | character varying(255)      |           |          |
22Indexes:
23    "todo_pkey" PRIMARY KEY, btree (id)

Sampai tahap ini database kita sudah selesai πŸŽ‰. Selanjutnya kita dapat memulai untuk membuat API dengan operasi ke database ini.

Untuk kerapian code kita seperti halnya folder controller dan model, kita akan membuat dua folder tambahan yaitu repository dan service. Pembagian folder ini juga ditujukan untuk pembagian fungsi object Java kita nanti dan sudah sangat common dalam arsitektur project seperti ini. repository akan berfungsi sebagai implementasi JPA dan mengandung logic yang berhubungan dengan operasi database. Sedangkan service adalah dimana kita menempatkan business logic yang lebih luas.


Sederhananya, alur program kita akan berjalan seperti ini.

Project's Program Flow image
Project's Program Flow

Setelah membuat kedua folder tersebut, struktur folder project kita akan menjadi seperti ini.

1.
2β”œβ”€β”€ src
3β”‚   β”œβ”€β”€ main
4β”‚   β”‚   β”œβ”€β”€ java
5β”‚   β”‚   β”‚   └── com/bca/adam
6β”‚   β”‚   β”‚       β”œβ”€β”€ model
7β”‚   β”‚   β”‚       β”œβ”€β”€ repository
8β”‚   β”‚   β”‚       β”œβ”€β”€ controller
9β”‚   β”‚   β”‚       └── service
10β”‚   β”‚   └── resources

Berdasarkan penjelasan sebelumnya, ada 4 endpoints API yang akan kita buat. Personally, saya merasa lebih mudah untuk membuat API yang sifatnya inquiry atau menampilkan data terlebih dahulu dibanding API untuk menambah atau mengubah data. Untuk itu, kita akan terlebih dahulu membuat API untuk mendapatkan daftar to-do.

Untuk memulai membuat API, lebih dahulu kita akan membuat operasi database yang diperlukan. Untuk mendapatkan list record data to-do, SQL query yang kemungkinan kita akan gunakan adalah SELECT * FROM todo;. Dengan bantuan JPA, kita tidak perlu lagi membuat query seperti ini. Di dalam folder repository, kita buat interface TodoRepository.java, bukan class ya. Kenapa bukan class? Karena kita akan memanfaatkan JPA yang sudah disediakan oleh Spring JPA yaitu JpaRepository yang adalah interface.

java

1package com.bca.adam.repository;
2
3import java.util.List;
4import java.util.Optional;
5import java.util.UUID;
6
7import com.bca.adam.model.Todo;
8
9import org.springframework.data.jpa.repository.JpaRepository;
10
11public interface TodoRepository extends JpaRepository<Todo, String> {
12    //get todo by ID
13    Optional<Todo> findById(UUID id);
14
15    //get all todo, similar with "SELECT * FROM todo;"
16    //this actually included in JpaRepository
17    //it is written here just to avoid confusion for beginners
18    List<Todo> findAll();
19}
20

Pada interface ini, kita akan membuat dua operasi database, yaitu mendapatkan semua list to-do dan mendapatkan spesifik task to-do berdasarkan ID. Tujuan kedua fungsi ini akan lebih jelas fungsinya saat nanti kita buat UI nya di Chapter 2.


Sebenarnya, kita bahkan tidak perlu menambahkan fungsi apapun di interface ini karena JpaRepository sudah memiliki operasi database yang cukup lengkap. Daftar operasi yang sudah disediakan dapat fellowdevs lihat pada definisi interface JpaRepository.

JpaRepository interface has provided basic CRUD operations image
JpaRepository interface has provided basic CRUD operations

Apabila kita tetap membutuhkan operasi database yang tidak ada dalam interface tersebut, kita dapat dengan mudah membuatnya melalui penamaan fungsi yang semantic. Strategi Spring JPA ini disebut juga dengan Derived Query Method yang memudahkan kita dalam membuat query berdasarkan nama fungsi.

Misalnya findAll(), tanpa mengetahui apa implementasinya dari fungsi ini, kita sudah langsung dapat mengerti bahwa fungsi ini bermakna "dapatkan semua data" dari sebuah entity. Saya sering menyebut hal ini sebagai magic-nya Spring JPA.


Untuk lebih mengerti mengenai bagaimana membuat custom query yang lebih kompleks dengan derived query methods dan memahami bagaimana cara kerjanya, fellowdevs dapat membaca artikel ini.


Selanjutnya, kita buat class baru di dalam folder service, dengan nama TodoService.java. Di dalam class ini, biasanya akan mengandung banyak logic yang berhubungan dengan business logic atau bahkan data manipulation. Tetapi karena saat ini kita belum membutuhkan hal tersebut, maka class ini dapat dibilang hanya sebagai perantara dari controller ke repository saja.

java

1package com.bca.adam.service;
2
3import java.util.ArrayList;
4import java.util.Date;
5import java.util.List;
6import java.util.Optional;
7import java.util.UUID;
8
9import com.bca.adam.model.Todo;
10import com.bca.adam.repository.TodoRepository;
11
12import org.springframework.beans.factory.annotation.Autowired;
13import org.springframework.stereotype.Service;
14
15import lombok.extern.slf4j.Slf4j;
16
17@Service
18@Slf4j
19public class TodoService {
20
21    @Autowired
22    TodoRepository todoRepo;
23
24    public List<Todo> getAll() {
25        List<Todo> todos = new ArrayList<>();
26        todoRepo.findAll().forEach(todos::add);
27
28        if (todos.isEmpty()) {
29            log.info("You have no todos!");
30            return new ArrayList<>();
31        }
32
33        return todos;
34    }
35}
  • @Service adalah anotasi untuk memberi tahu Spring bahwa class ini adalah sebuah component penting, yaitu Service.
  • @Slf4j kita tambahkan untuk menambahkan fungsi logging pada aplikasi kita secara otomatis dan mudah. Anotasi ini kita dapatkan dari library Lombok yang kita tambahkan pada pom.xml. Bayangkan kalau kita tidak menggunakan Lombok, akan lebih banyak lagi memakan waktu dan langkah untuk sekedar membuat fungsi logging.
  • @Autowired adalah anotasi yang sangat penting dalam Spring. Salah satu keunggulan Spring adalah IoC (Inversion of Control) atau sering juga disebut Dependency Injection (DI). Anotasi @Autowired memungkinkan kita dengan mudah untuk melakukannya.
Konsep Dependency Injection sangatlah penting untuk fellowdevs mengerti dan pahami, tidak hanya dalam Spring atau Java namun konsep pemrograman secara umum. Memahami konsep ini akan sangat membantu kalian di kemudian hari dan saya sangat merekomendasikan fellowdevs untuk mempelajarinya. Silakan dapat kalian pelajari lebih lanjut di sini.
  • Sisa code lainnya adalah OOP biasa, yaitu memanggil fungsi dari TodoRepository, melakukan validasi kosong/tidak, kemudian mengembalikannya sebagai return value fungsi getAll().


Service dan repository kita sudah selesai, selanjutnya kita akan membuat controller-nya. Tentu fellowdevs sudah mengerti ya konsep controller melalui penjelasan sebelumnya. Di dalam folder controller, kita tambahkan class baru yaitu TodoController.java.

java

1package com.bca.adam.controller;
2
3import java.util.List;
4
5import org.springframework.beans.factory.annotation.Autowired;
6import org.springframework.http.HttpStatus;
7import org.springframework.http.ResponseEntity;
8import org.springframework.web.bind.annotation.CrossOrigin;
9import org.springframework.web.bind.annotation.GetMapping;
10import org.springframework.web.bind.annotation.RequestMapping;
11import org.springframework.web.bind.annotation.RestController;
12
13import com.bca.adam.model.Todo;
14import com.bca.adam.service.TodoService;
15
16import io.swagger.annotations.Api;
17import lombok.extern.slf4j.Slf4j;
18
19@CrossOrigin(origins = "*")
20@RestController
21@RequestMapping("/todo")
22@Slf4j
23public class TodoController {
24
25    @Autowired
26    TodoService todoService;
27
28    @GetMapping("")
29    public ResponseEntity<List<Todo>> getAll() {
30        log.info("Getting todo list...")
31        try {
32            List<Todo> todos = todoService.getAll();
33            if (todos.isEmpty()) {
34                return new ResponseEntity<>(HttpStatus.NO_CONTENT);
35            }
36            return new ResponseEntity<>(todos, HttpStatus.OK);
37        } catch (Exception e) {
38            e.printStackTrace();
39            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
40        }
41    }
42}
43
  • Request menuju ke TodoController kita arahkan menggunakan path /todo melalui anotasi @RequestMapping("/todo")
  • @CrossOrigin ditambahkan untuk mencegah error CORS.
Di sini saya memberikan anotasi @CrossOrigin dengan value *, tapi pada project sebenarnya fellowdevs harus menggantinya dengan eligible client kalian ya!


That's it
! Sekarang mari kita test API yang sudah kita buat ini. Kali ini saya akan menggunakan Postman. Jangan lupa start aplikasi kita dan masukkan URL aplikasi kita ke path controller inquiry list todo kita yaitu localhost:8080/adam/todo/.

Hit API with Postman image
Hit API with Postman

Response yang akan kita dapatkan adalah .... kosong! Hmmm, apakah program kita error? Mari kita inspeksi. HTTP status yang kita dapatkan adalah 204 No Content, dimana kalau kita cek ke controller, kita memang mengembalikan HttpStatus.NO_CONTENT apabila tidak ada data. Kemudian kita juga bisa mengecek ke logging kita, bahwa kita dapatkan log:

1INFO 12240 --- [nio-8080-exec-3] com.bca.adam.service.TodoService         : You have no todos!

Ya! Kita juga membuatnya di service kita untuk menuliskan log tersebut apabila tidak ada data ditemukan. Artinya tidak ada yang error dengan program kita. Setelah diingat-ingat, kita memang belum menambahkan data apapun ke dalam database kita tadi. Nah untuk sementara, mari kita tambahkan melalui direct query ke dalam database.

mysql

1INSERT INTO public.todo (id,title) VALUES ('e8e40842-eb4d-4d0c-8d55-f5124c3051fa'::uuid,'Cuci baju');
UUID saya dapatkan dengan menggunakan generator online.

Setelah kita tambahkan data tersebut ke dalam database kita dan hit ulang API kita, maka kita akan mendapatkan response seperti ini.

json

1[
2    {
3        "id": "e8e40842-eb4d-4d0c-8d55-f5124c3051fa",
4        "title": "Cuci baju",
5        "createdDate": "2023-02-10T10:00:21.527+00:00",
6        "modifiedDate": null,
7        "done": false
8    }
9]

Nice! Sekarang API get to-do list kita sudah selesai.

Selanjutnya kita akan membuat API untuk menambahkan data to-do. Seperti yang sudah saya sebutkan sebelumnya bahwa Spring JPA sudah menyediakan untuk kita fungsi database dasar yaitu CRUD. Maka dari itu, kita tidak akan menambahkan fungsi ini secara manual pada repository kita melainkan kita cukup menambahkan fungsi pada service kita yang akan memanggil fungsi save() yang telah disediakan Spring tersebut.

java

1// TodoService.java
2// .... previous lines
3    public Todo addTodo(Todo todo) {
4        return todoRepo.save(new Todo(todo.getTitle()));
5    }

Sedikit penjelasan mengenai code ini adalah:

  • Fungsi addTodo akan menerima parameter berupa object class Todo. Karena object ini sudah serupa dengan generic type yang kita tentukan saat mengimplementasikan JPA yakni JpaRepository<Todo, String>, maka Spring akan dengan mudah memproses object ini untuk di-map ke dalam entity.
  • Fungsi save yang dipanggil melalui todoRepo adalah bawaan dari JpaRepository.
  • Untuk tahap ini, kita hanya akan mengambil field title saja dari parameter yang diterima. Oleh karena itu saya membuat new object dengan menggunakan constructor yang sudah saya definisikan pada class Todo sebelumnya.

Langkah selanjutnya yaitu membuat controller untuk API addTodo ini.

java

1// TodoController.java
2// ... previous lines
3    /*
4     * basically you might want to use DTO object to store the request and response
5     * payload instead of the model itself
6     */
7    @PostMapping("")
8    public ResponseEntity<Todo> addTodo(@RequestBody Todo todo) {
9        try {
10            if (todo.getTitle() == null || !StringUtils.hasText(todo.getTitle())) {
11                log.info("To add item to your todo list, you must give it a title!");
12                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
13            }
14            return new ResponseEntity<>(todoService.addTodo(todo), HttpStatus.OK);
15        } catch (Exception e) {
16            e.printStackTrace();
17            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
18        }
19    }
20

Tidak ada yang baru di baris code ini selain penggunaan anotasi @RequestBody. Anotasi ini kita gunakan untuk menerima request body yang dikirimkan oleh client. Apabila kalian belum mengerti apa itu request body, bisa pelajari dulu tentang konsep API ya.


Parameter yang kita terima sebagai request body adalah object Todo. Nah, saya beri bocoran sedikit. Pada dasarnya proses pertukaran data yang terjadi pada API kita akan menggunakan format JSON. Tidak terkecuali dalam mengirimkan request body ini. Nah pertanyaannya, bagaimana mungkin JSON yang dikirimkan tersebut dapat diterima sebagai object Todo yang notabene adalah Java class? Di sinilah salah satu kemudahan yang diberikan oleh Spring.


Melalui anotasi @RequestBody, JSON string tersebut akan secara otomatis di-deserialize-kan untuk dapat dikonversi menjadi object Todo. Salah satu syaratnya adalah dengan menggunakan nama field yang sama. Apabila ada field yang tidak sama dengan object yang diminta, pilihannya tergantung pada kita. Apakah kita akan tetap mengolah request tersebut atau return error. Untuk menunjukannya pada fellowdevs saya akan menambahkan field tambahan yang tidak ada pada object Todo nanti.

Untuk cara yang lebih "proper", kita juga dapat menggunakan model DTO (Data Transfer Object), namun untuk mempersingkat tulisan ini saya tidak membahasnya di sini. Fellowdevs dapat membaca mengenai DTO di sini.


Oke, setelah menyimpan semua file tersebut, sekarang saatnya untuk mencobanya. Melalui Postman, kita kirimkan request body sebagai berikut.

json

1{
2    "title": "Kerjain tugas BCAITWORKS!",
3    "unmapped_field": "i'm not gonna be mapped!"
4}

Jangan lupa untuk mengubah request method-nya menjadi POST ya. Dan apabila kita send request ini, maka kita akan mendapatkan response seperti berikut.

Adding to-do image
Adding to-do

Nah, dapat fellowdevs lihat sendiri bahwa request tersebut berhasil terkirim dan data to-do berhasil ditambahkan. Untuk mengeceknya, kita dapat memanggil API get to-do sebelumnya atau inquiry ke database langsung.

json

1// returned todo list after adding data
2[
3    {
4        "id": "e8e40842-eb4d-4d0c-8d55-f5124c3051fa",
5        "title": "Cuci baju",
6        "createdDate": "2023-02-10T10:00:21.527+00:00",
7        "modifiedDate": null,
8        "done": false
9    },
10    {
11        "id": "ab7ec7ce-e159-4e53-8647-edb555281876",
12        "title": "Kerjain tugas BCAITWORKS!",
13        "createdDate": "2023-02-11T18:39:22.598+00:00",
14        "modifiedDate": null,
15        "done": false
16    }
17]

Ingat bahwa kita menambahkan field unmapped_field yang tidak ada pada object Todo. Seperti yang saya bilang pilihannya kembali ke kita sebagai developer. Apakah kita akan menolak request ini karena mengandung unsur yang tidak kita kenal? Atau akan kita biarkan namun tidak diproses? Pilihannya bermacam-macam. Dalam hal ini, saya hanya mengambil field title dan melakukan validasi terhadapnya. Silakan fellowdevs dapat memodifikasi code tersebut apabila ingin melakukan hal yang berbeda, anggap saja belajar 😁

Pada API addTodo, kita melakukan validasi blank/kosong terhadap field title dan mengembalikan error. Silakan dapat fellowdevs coba ya!

Nah selanjutnya kita akan membuat API kita yang terakhir yaitu fungsi untuk mengubah data to-do. Dalam hal ini, data yang akan diubah adalah status dari sebuah data to-do yaitu misalnya sebuah to-do sudah selesai dikerjakan, maka kita cukup meng-update statusnya menjadi Done. Lalu apabila to-do tersebut hendak dikerjakan kembali maka user dapat mengubah menjadi Undone.


Kita akan menggunakan kembali fungsi save dari JpaRepository. Meskipun fungsi save ini kita tadi gunakan untuk membuat data baru, namun dapat juga kita gunakan untuk meng-update sebuah data. Spring JPA sudah cukup cerdas untuk membedakan apakah operasi yang ingin kita lakukan adalah insert atau update. Untuk meng-update data, kita perlu men-set terlebih dahulu data yang ingin diubah, biasanya kita akan lakukan terlebih dahulu inquiry seperti misalnya findById(id). Data yang kita dapatkan dari operasi ini cukup kita set field mana yang ingin diubah. Syarat utamanya, field ID yang diberi anotasi @Id pada entity class harus terisi (tidak null) maka Spring JPA akan menganggap operasi tersebut adalah operasi update.

java

1// TodoService.java
2// ... previous lines
3    public Todo modifyTodo(String id) {
4        Optional<Todo> todo = todoRepo.findById(UUID.fromString(id));
5        if (todo.isPresent()) {
6            Todo existTodo = todo.get();
7            existTodo.setDone(!existTodo.isDone());
8            existTodo.setModifiedDate(new Date());
9            return todoRepo.save(existTodo);
10        } else {
11            throw new IllegalArgumentException(String.format("data with id %s not found.", id));
12        }
13    }

Seperti yang kalian lihat, sebelum mengubah data saya terlebih dahulu mengambil data berdasarkan ID. Kemudian apabila data dengan ID tersebut ditemukan, maka saya akan gunakan sebagai object dan set field isDone dan modifiedDate untuk diubah, barulah saya panggil fungsi save dari JpaRepository. Sedangkan apabila data dengan ID tersebut tidak ditemukan, maka saya akan mengembalikan error karena tidak dapat mengubah data yang tidak ditemukan.

java

1// TodoController.java
2// ... previous lines
3    @PutMapping("")
4    public ResponseEntity<Todo> modifyTodo(@RequestBody Map<String, String> body) {
5        try {
6            if (body.get("id") == null)
7                return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
8
9            return new ResponseEntity<>(todoService.modifyTodo(body.get("id")), HttpStatus.OK);
10        } catch (Exception e) {
11            e.printStackTrace();
12            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
13        }
14    }

Kemudian kita tambahkan code di atas pada TodoController.java. Untuk melakukan update data, biasanya digunakan HTTP method PUT atau PATCH. Pada contoh ini saya akan gunakan PUT saja.

Perbedaan PUT dan PATCH dapat dibaca di sini.


Kalau fellowdevs perhatikan, parameter pada @RequestBody yang diterima saya tidak lagi menggunakan object Todo seperti pada API addTodo melainkan menggunakan tipe data Map<String, String>. Hal ini saya maksudkan untuk menunjukan kepada fellowdevs bahwa kita juga dapat menggunakan jalur "konvensional" semacam ini. Namun kekurangannya, kita harus mengolah object Map ini secara manual apabila ingin digunakan atau bahkan dikonversi ke object seperti entity atau DTO.


Save semua file tersebut dan kita tes API ini. Karena kita memerlukan ID untuk dapat mengubah data, maka untuk saat ini fellowdevs dapat mengambil ID tersebut dari existing data di database terlebih dahulu. Misalnya saya ingin mengubah data ini:

json

1    {
2        "id": "ab7ec7ce-e159-4e53-8647-edb555281876",
3        "title": "Kerjain tugas BCAITWORKS!",
4        "createdDate": "2023-02-11T18:39:22.598+00:00",
5        "modifiedDate": null,
6        "done": false
7    }

Maka saya gunakan ID tersebut dan mengirimkannya pada request body API modifyTodo.

Updating to-do item image
Updating to-do item

Setelah kita send, maka status data to-do dengan ID tersebut akan berubah menjadi Done (done= true). Sedangkan apabila saya resend request yang sama, status data ini akan kembali menjadi Undone (done= false). Hal ini sesuai dengan spesifikasi API yang saya buat untuk contoh sederhana ini.

json

1{
2    "id": "ab7ec7ce-e159-4e53-8647-edb555281876",
3    "title": "Kerjain tugas BCAITWORKS!",
4    "createdDate": "2023-02-11T18:39:22.598+00:00",
5    "modifiedDate": "2023-02-12T06:24:00.989+00:00",
6    "done": true // this switches to false and true everytime the call happen according to current status
7}


Nah, setelah semua tes kita berhasil dan tidak ada error, maka API untuk aplikasi yang akan kita bangun sudah selesai πŸ‘!

Setelah kita membuat API untuk aplikasi to-do sederhana kita ini, tahapan selanjutnya adalah membuat aplikasi frontend yang menampilkan UI agar aplikasi kita dapat digunakan. Yuk, lanjut ke Chapter 2! πŸ‘‹

Nah, setelah kita membuat API aplikasi kita, tahap selanjutnya adalah mendokumentasikan API tersebut menjadi sebuah dokumen yang dapat dikonsumsi baik itu bagi frontend developer, next developer, atau bahkan kita sendiri sebagai sumber informasi/landasan spesifikasi API tersebut dibuat.

Apabila pembagian tim kerja kita terpisah menjadi backend dan frontend developer, maka kita perlu men-deliver dokumen tersebut kepada frontend developer supaya mereka dapat mengetahui bagaimana menggunakan API yang sudah kita buat.


Bagaimana caranya? Paling mudahnya, kita dapat membuat dokumen dalam bentuk text seperti Word atau Excel dan menjelaskan tentang apa saja yang dibutuhkan untuk dapat menggunakan API kita. Nah persoalannya, membuat dokumentasi semacam ini adalah pekerjaan yang membosankan. Apalagi kalau API kita sering mengalami perubahan, maka kita juga akan berulang kali membuat dokumen semacam ini.


Nah, kabar baik bagi kita semua, Swagger hadir untuk membantu kita dalam hal ini. Dengan Swagger, kita dapat memiliki dokumentasi API yang dengan mudah di-generate sesuai dengan code kita, lengkap, tampilan yang bagus dan ter-standar, serta interaktif! Interaktif yang saya maksud adalah bahkan kita bisa mencoba endpoints API dengan klik. Hal ini memungkinkan penerima dokumentasi dapat langsung melakukan verifikasi dengan cepat (rapid testing) apakah API yang kita sediakan dapat mereka pergunakan atau tidak. Hebat bukan?

Swagger Preview image
Swagger Preview

Saya akan bahas secara terpisah mengenai bagaimana cara kita dapat mengintegrasikan Swagger ke project kita ini. Silakan fellowdevs dapat membaca pada artikel ini.

Reactions


Comments


More articles

If you enjoyed this article, why not check my other posts?

Docker: The Key to Consistent and Reliable App Deployment image

Docker: The Key to Consistent and Reliable App Deployment

0 viewed

Published at 1/4/2023, 11:51:00 AM

GraphQL now and thank me later! image

GraphQL now and thank me later!

0 viewed

Published at 9/12/2022, 4:36:00 AM

Containers vs VMs? What differs? image

Containers vs VMs? What differs?

0 viewed

Published at 11/22/2022, 6:10:00 AM