0% menganggap dokumen ini bermanfaat (0 suara)
48 tayangan63 halaman

Materi 3

Diunggah oleh

Lavina Safitri
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai TXT, PDF, TXT atau baca online di Scribd
0% menganggap dokumen ini bermanfaat (0 suara)
48 tayangan63 halaman

Materi 3

Diunggah oleh

Lavina Safitri
Hak Cipta
© © All Rights Reserved
Kami menangani hak cipta konten dengan serius. Jika Anda merasa konten ini milik Anda, ajukan klaim di sini.
Format Tersedia
Unduh sebagai TXT, PDF, TXT atau baca online di Scribd
Anda di halaman 1/ 63

Membangun Web Service menggunakan Node.

js

Membangun Web Server menggunakan Node.js secara Native

Menyiapkan Project
Sebelum membuat Web Server, buatlah proyek Node.js terlebih dahulu. Silakan buat
folder baru di dalam C -> javascript-projects (Windows) atau home -> javascript-
project (Linux dan Mac) dengan nama “nodejs-web-server”. Setelah itu, buka folder
tersebut menggunakan VSCode.

Buka Terminal dan tuliskan perintah:

npm init --y

Mungkin Anda bertanya mengapa terdapat --y di akhir perintahnya? --y pada akhir
perintah tersebut berfungsi untuk menjawab seluruh pertanyaan yang diberikan NPM
ketika membuat proyek baru dengan jawaban/nilai default.

Jika Anda lebih suka menjawab pertanyaan-pertanyaan tersebut secara manual, silakan
hapus --y pada perintah tersebut.

Setelah membuat proyek Node.js, pastikan di dalam proyek nodejs-web-server terdapat


berkas package.json.

Lanjut kita buat berkas JavaScript baru, karena kita hendak membuat server, maka
beri nama berkas tersebut server.js.

Di dalamnya tuliskan kode JavaScript berikut:

console.log('Halo, kita akan belajar membuat server');

Kemudian buka berkas package.json dan tambahkan runner script seperti ini:

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
}

Sebenarnya Anda bisa menghapus runner script test. Karena script tersebut tidak
kita gunakan. Jadi, runner script hanya memiliki nilai start saja.

"scripts": {
"start": "node server.js"
}

Simpan seluruh perubahan pada berkas yang ada. Kemudian buka terminal dan jalankan
perintah:

npm run start

Bila konsol menampilkan pesan “Halo, kita akan belajar membuat server”, Selamat!
Persiapan proyek kita sudah selesai.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Membuat HTTP Server


Pengembangan back-end adalah hal prioritas untuk Node.js. Ia andal dalam membangun
aplikasi back-end, salah satunya web server alias sebuah komputer yang dapat
menangani dan menanggapi permintaan dari client. Node.js menyediakan core modules
http untuk membangun web server.

const http = require('http');

HTTP module memiliki banyak member seperti objek, properti, atau method yang
berguna untuk melakukan hal-hal terkait protokol HTTP. Salah satu member yang
penting untuk kita ketahui sekarang adalah method http.createServer().

Sesuai namanya, method ini berfungsi untuk membuat HTTP server yang merupakan
instance dari http.server. Method ini menerima satu parameter custom callback yang
digunakan sebagai request listener. Di dalam request listener inilah logika untuk
menangani dan menanggapi sebuah request dituliskan.

const http = require('http');

/**
* Logika untuk menangani dan menanggapi request dituliskan pada fungsi ini
*
* @param request: objek yang berisikan informasi terkait permintaan
* @param response: objek yang digunakan untuk menanggapi permintaan
*/
const requestListener = (request, response) => {

};

const server = http.createServer(requestListener);

Request listener memiliki 2 parameter, yakni request dan response. Seperti yang
tertulis pada contoh kode di atas, request merupakan objek yang menyimpan informasi
terkait permintaan yang dikirimkan oleh client. Di dalam objek ini kita bisa
melihat alamat yang diminta, data yang dikirim, ataupun HTTP metode yang digunakan
oleh client.

Sementara itu, response merupakan objek yang digunakan untuk menanggapi permintaan.
Melalui objek ini kita bisa menentukan data yang diberikan, format dokumen yang
digunakan, kode status, atau informasi response lainnya.

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');

response.statusCode = 200;
response.end('<h1>Halo HTTP Server!</h1>');
};

Kode di atas merupakan contoh logika yang bisa dituliskan di dalam request
listener. Request listener akan menanggapi setiap permintaan dengan dokumen HTML,
kode status 200, dan menampilkan konten “Halo HTTP Server!”.

Lalu, bagaimana caranya agar server selalu sedia menangani permintaan yang masuk?
Setiap instance dari http.server juga memiliki method listen(). Method inilah yang
membuat http.server selalu standby untuk menangani permintaan yang masuk dari
client. Setiap kali ada permintaan yang masuk, request listener akan tereksekusi.

Method listen() dapat menerima 4 parameter, berikut detailnya:

port (number) : jalur yang digunakan untuk mengakses HTTP server.


hostname (string) : nama domain yang digunakan oleh HTTP server.
backlog (number) : maksimal koneksi yang dapat ditunda (pending) pada HTTP server.
listeningListener (function) : callback yang akan terpanggil ketika HTTP server
sedang bekerja (listening).

Namun, keempat parameter di atas bersifat opsional. Kita bisa memberikan nilai port
saja, atau kombinasi apa pun dari keempatnya. Hal itu tergantung terhadap kebutuhan
Anda. Namun lazimnya, ketika memanggil method listen() kita memberikan nilai port,
hostname, dan listeningListener.

const port = 5000;


const host = 'localhost';

server.listen(port, host, () => {


console.log(`Server berjalan pada http://${host}:${port}`);
});

Latihan Membuat HTTP Server


Nah, sekarang giliran kita praktikan pada proyek nodejs-web-server. Silakan hapus
kode yang ada pada server.js dan ganti dengan kode untuk membuat http server
seperti ini:

const http = require('http');

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');

response.statusCode = 200;
response.end('<h1>Halo HTTP Server!</h1>');
};

const server = http.createServer(requestListener);

const port = 5000;


const host = 'localhost';

server.listen(port, host, () => {


console.log(`Server berjalan pada http://${host}:${port}`);
});

Setelah itu, jalankan perintah npm run start pada Terminal. Jika server berhasil
dijalankan, maka Anda akan melihat pesan ‘Server berjalan pada
http://localhost:5000’.

Untuk pengguna sistem operasi Windows, bila pop-up di bawah ini muncul, pilih saja
“Allow access”.

Selamat! Anda berhasil membuat HTTP Server pertama menggunakan Node.js. Anda bisa
coba melakukan request pada server tersebut melalui cURL seperti ini:

Terminal/CMD
curl -X GET http://localhost:5000/

Anda juga bisa mencoba langsung pada browser dengan mengunjungi halaman
http://localhost:5000/.

Good Job! Akhirnya Anda berhasil membuat web server pertama menggunakan Node.js!
Walau masih sangat sederhana, namun hal tersebut merupakan hal pertama yang
dilakukan oleh back-end developer dalam meniti karirnya.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Method/Verb Request
Web server yang sudah kita buat pada latihan sebelumnya sudah berhasil merespons
dan menampilkan data dalam dokumen HTML. Namun, tahukah Anda bahwa web server yang
kita buat belum mengenali sepenuhnya permintaan yang diberikan oleh client?

Maksudnya, meskipun client meminta dengan path atau method yang berbeda, server
akan merespons dengan data yang sama. Server kita saat ini tidak peduli permintaan
datang seperti apa, dia akan mengembalikan data yang sama. Anda bisa mencobanya
sendiri melalui cURL dengan menggunakan HTTP method yang berbeda.

curl -X POST http://localhost:5000


// output: <h1> Halo HTTP Server!</h1>
curl -X PUT http://localhost:5000
// output: <h1> Halo HTTP Server!</h1>
curl -X DELETE http://localhost:5000
// output: <h1> Halo HTTP Server!</h1>
curl -X GET http://localhost:5000
// output: <h1> Halo HTTP Server!</h1>

Ketika mencobanya pastikan HTTP Server Anda sedang berjalan. Bila dalam keadaan
terhenti, jalankan kembali server dengan perintah npm run start pada Terminal
proyek nodejs-web-server.

Hal tersebut wajar karena kita memang belum menuliskan logika dalam menangani
permintaan dari method yang berbeda. Lalu, bagaimana caranya agar bisa melakukan
hal tersebut?

Fungsi request listener menyediakan dua parameter yakni request dan response. Fokus
ke parameter request, parameter ini merupakan instance dari http.ClientRequest yang
memiliki banyak properti di dalamnya.

Melalui properti-propertinya ini kita dapat mengetahui seperti apa karakteristik


dari permintaan HTTP yang dilakukan oleh client. Seperti method yang digunakan,
path atau alamat yang dituju, data yang dikirimkan (bila ada), dan informasi lain
mengenai permintaan tersebut.

Untuk mendapatkan nilai method dari request, gunakanlah properti request.method


seperti ini:

const requestListener = (request, response) => {


const method = request.method;
};

Atau, Anda bisa menggunakan cara yang lebih clean dengan menggunakan object
destructuring seperti ini:

const requestListener = (request, response) => {


const { method } = request;
};

Properti method bernilai tipe method dalam bentuk string. Nilainya dapat berupa
“GET”, “POST”, “PUT”, atau method lainnya sesuai dengan yang client gunakan ketika
melakukan permintaan. Dengan memiliki nilai method, kita bisa memberikan respons
berbeda berdasarkan tipe method-nya.

const requestListener = (request, response) => {


const { method } = request;

if(method === 'GET') {


// response ketika GET
}

if(method === 'POST') {


// response ketika POST
}

// Anda bisa mengevaluasi tipe method lainnya


};

Sekali lagi, tidak hanya properti method, objek request kaya akan properti dan
fungsi lain di dalamnya. Anda dapat mengeksplorasi properti atau fungsi lainnya
pada halaman dokumentasi Node.js tentang HTTP Client Request.

Latihan Handling Request


Sekarang Anda sudah tahu kan bagaimana cara mendapatkan nilai method dari
permintaan client? Yuk terapkan pada web server yang sudah kita buat.

Pada latihan ini, kita akan melakukan konfigurasi agar server mengembalikan respons
dengan kata “hello” dalam berbagai bahasa sesuai method yang digunakan client.

Silakan buka kembali berkas server.js pada proyek nodejs-web-server. Lalu,


sesuaikan respons berdasarkan request method yang dilakukan oleh client.

const http = require('http');

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method } = request;

if(method === 'GET') {


response.end('<h1>Hello!</h1>');
}

if(method === 'POST') {


response.end('<h1>Hai!</h1>');
}

if(method === 'PUT') {


response.end('<h1>Bonjour!</h1>');
}

if(method === 'DELETE') {


response.end('<h1>Salam!</h1>');
}
};

const server = http.createServer(requestListener);

const port = 5000;


const host = 'localhost';
server.listen(port, host, () => {
console.log(`Server berjalan pada http://${host}:${port}`);
});

Simpan perubahan pada berkas server.js; jalankan ulang server dengan perintah npm
run start; dan coba lakukan permintaan ke server dengan menggunakan method yang
berbeda melalui cURL.

Hasilnya akan tampak seperti ini:

curl -X GET http://localhost:5000


// output: <h1>Hello!</h1>
curl -X POST http://localhost:5000
// output: <h1>Hai!</h1>
curl -X PUT http://localhost:5000
// output: <h1>Bonjour!</h1>
curl -X DELETE http://localhost:5000
// output: <h1>Salam!</h1>

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Body Request
Ketika client melakukan permintaan dengan method POST atau PUT, biasanya permintaan
tersebut memiliki sebuah data yang disimpan pada body request. Data pada body bisa
berupa format teks, JSON, berkas gambar, atau format lainnya. Data tersebut
nantinya digunakan oleh server untuk diproses di database atau disimpan dalam
bentuk objek utuh.

Ketahuilah bahwa http.clientRequest merupakan turunan dari readable stream, yang di


mana untuk mendapatkan data body akan sedikit sulit dibandingkan dengan mendapatkan
data header. Data di body didapatkan dengan teknik stream, seperti yang sudah kita
ketahui, teknik ini memanfaatkan EventEmitter untuk mengirimkan bagian-bagian
datanya. Dalam kasus http.clientRequest event data dan end-lah yang digunakan untuk
mendapatkan data body.

Berikut adalah contoh bagaimana mendapatkan data body:

const requestListener = (request, response) => {


let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
});
};

Mari kita bedah kodenya.

Pertama, kita deklarasikan variabel body dan inisialisasikan nilainya dengan array
kosong. Ini berfungsi untuk menampung buffer pada stream.
Lalu, ketika event data terjadi pada request, kita isi array body dengan chunk
(potongan data) yang dibawa callback function pada event tersebut.
Terakhir, ketika proses stream berakhir, maka event end akan terbangkitkan. Di
sinilah kita mengubah variabel body yang sebelumnya menampung buffer menjadi data
sebenarnya dalam bentuk string melalui perintah Buffer.concat(body).toString().

Huft! Cukup melelahkan yah untuk mendapatkan data melalui teknik stream. Guna
memantapkan pemahaman, mari kita praktikan pada proyek web server sebelumnya.

Latihan Mendapatkan Body Request


Di latihan sebelumnya, web server yang kita buat sudah berhasil merespons sesuai
request method yang dilakukan client. That’s nice!

Nah, di latihan kali ini kita akan coba mendapatkan data pada body request ketika
client mengirimkan request menggunakan method POST.

Buatlah web server merespons permintaan method POST dengan menampilkan sapaan dan
nama berdasarkan data body yang dikirim oleh client. Bila client mengirimkan nama
“Dicoding”, maka respons akan menampilkan “Hai, Dicoding!”.

Client akan mengirimkan data nama tersebut menggunakan format JSON seperti ini:

{ "name": "Dicoding" }

Namun sebelum itu, agar latihan lebih fokus terhadap bagaimana mendapatkan data
pada body, kita hapus dulu logika method yang sebenarnya belum kita butuhkan,
seperti PUT dan DELETE.

Jadi, silakan buka berkas server.js pada proyek nodejs-web-server dan hapuslah
logika method PUT dan DELETE. Sehingga, berkas server.js tampak lebih ringkas
seperti ini:

const http = require('http');

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method } = request;

if(method === 'GET') {


response.end('<h1>Hello!</h1>');
}

if(method === 'POST') {


response.end('<h1>Hai!</h1>');
}
};

const server = http.createServer(requestListener);

const port = 5000;


const host = 'localhost';

server.listen(port, host, () => {


console.log(`Server berjalan pada http://${host}:${port}`);
});

Selanjutnya, kita bisa mulai menuliskan logika stream di dalam blok POST.

if(method === 'POST') {


let body = [];
request.on('data', (chunk) => {
body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
response.end(`<h1>Hai, ${body}!</h1>`);
});
}

Perhatikan kode di atas! Kita memindahkan proses respons di dalam callback event
end. Hal ini diperlukan karena data body siap dikonsumsi hanya ketika event end
telah dibangkitkan. Dalam arti lain, server tidak akan mengirimkan respons bila
proses stream belum selesai.

Simpan perubahan pada berkas server.js; jalankan ulang server dengan perintah npm
run start; dan coba lakukan permintaan ke server dengan menggunakan method POST
melalui cURL seperti ini:

curl -X POST -H "Content-Type: application/json" http://localhost:5000 -d


"{\"name\": \"Dicoding\"}"

Server akan menanggapi dengan hasil berikut:

<h1>Hai, {"name": "Dicoding"}!</h1>

Tunggu, ini bukan hasil yang kita harapkan. Body masih bernilai data string JSON.
Data ini masih perlu kita olah lagi agar bisa mendapatkan nilai name yang
sebenarnya. Gunakanlah JSON.parse() untuk mengubah JSON string menjadi JavaScript
objek. Sesuaikan kembali kode pada blok POST menjadi seperti ini (lihat kode yang
ditebalkan):

if(method === 'POST') {


let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
const { name } = JSON.parse(body);
response.end(`<h1>Hai, ${name}!</h1>`);
});
}

Simpan perubahan pada berkas server.js; jalankan ulang server dengan perintah npm
run start; dan coba lagi lakukan permintaan ke server dengan menggunakan method
POST.

curl -X POST -H "Content-Type: application/json" http://localhost:5000 -d


"{\"name\": \"Dicoding\"}"

maka output-nya akan:

<h1>Hai, Dicoding!</h1>

Voila! Inilah hasil yang kita harapkan! Anda bisa kirimkan permintaan POST lain
dengan data nama Anda sendiri. Cobalah, apakah hasilnya sesuai atau tidak?

curl -X POST -H "Content-Type: application/json" http://localhost:5000 -d


"{\"name\": \"Dimas\"}"

// output: <h1>Hai, Dimas!</h1>

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Routing Request
Ketika menangani request, hal yang perlu kita cek selain method adalah URL atau
alamat yang dituju dari request tersebut. Sebagai contoh, ketika kita mengunjungi
dicoding.com dan dicoding.com/about, tentu hasil yang kita terima dari server akan
berbeda, bukan?

Request ke dicoding.com akan menampilkan homepage Dicoding, sedangkan


dicoding.com/about akan menampilkan halaman tentang Dicoding. Teknik ini dinamakan
dengan routing. Routing merupakan istilah dalam menentukan respons server
berdasarkan path atau url yang diminta oleh client.

Dalam http.clientRequest, untuk mendapatkan nilai url sangatlah mudah, semudah kita
mendapatkan nilai request method yang digunakan.

const requestListener = (request, response) => {


const { url } = request;
};

Properti url akan mengembalikan nilai path secara lengkap tanpa host dan port yang
digunakan server. Contohnya, bila client meminta pada alamat
http://localhost:5000/about atau http://localhost:5000/about/, maka url akan
bernilai ‘/about’; bila meminta alamat http://localhost:5000 atau
http://localhost:5000/, maka url akan bernilai ‘/’.

Dengan mendapatkan nilai url, kita dapat merespons client sesuai dengan path yang
ia minta.

const requestListener = (request, response) => {


const { url } = request;

if(url === '/') {


// curl http://localhost:5000/
}

if(url === '/about') {


// curl http://localhost:5000/about
}

// curl http://localhost:5000/<any>
};

Kita juga bisa mengombinasikan evaluasi dengan method request. Alhasil, kita dapat
menentukan respons lebih spesifik lagi.

const requestListener = (request, response) => {


const { url, method } = request;

if(url === '/') {


if(method === 'GET') {
// curl -X GET http://localhost:5000/
}

// curl -X <any> http://localhost:5000/


}

if(url === '/about') {

if(method === 'GET') {


// curl -X GET http://localhost:5000/about
}

if(method === 'POST') {


// curl -X POST http://localhost:5000/about
}

// curl -X <any> http://localhost:5000/about


}

// curl -X <any> http://localhost:5000/<any>


};

Latihan Routing Request


Saatnya kita latihan lagi yuk! Karena saat ini Anda sudah paham bagaimana cara
menangani request berdasarkan URL yang diminta, mari buat web server kita agar
dapat menangani request yang lebih spesifik berdasarkan URL dan method request.
Berikut tugas atau ketentuan yang akan kita gunakan:

URL: ‘/’
Method: GET
Mengembalikan “Ini adalah homepage”.
Method: <any> (selain GET)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: ‘/about’
Method: GET
Mengembalikan “Halo! Ini adalah halaman about”.
Method: POST (dengan melampirkan data name pada body)
Mengembalikan “Halo, <name>! Ini adalah halaman about”.
Method: <any> (selain GET dan POST)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: <any> (selain / dan /about)
Method: <any>
Mengembalikan “Halaman tidak ditemukan!”.

Sudah paham? Huft, latihan kali ini sepertinya lebih menantang. Siapkan secangkir
kopi agar Anda tetap fokus dan mari kita mulai.

Pertama, agar kita dapat fokus pada hal routing. Beri komentar dulu kode logika di
dalam fungsi request listener yang sebelumnya kita buat.

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method } = request;

// if(method === 'GET') {


// response.end('<h1>Hello!</h1>');
// }

// if(method === 'POST') {


// let body = [];

// request.on('data', (chunk) => {


// body.push(chunk);
// });

// request.on('end', () => {
// body = Buffer.concat(body).toString();
// const { name } = JSON.parse(body);
// response.end(`<h1>Hai, ${name}!</h2>`);
// });
// }
};

Selanjutnya, kita ambil properti url dari request menggunakan teknik destructuring
object seperti mendapatkan nilai method. Lihat kode yang ditebalkan yah.

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method, url } = request;

/** Kode komentar disembunyikan agar lebih ringkas */


}

Good! Sekarang kita sudah dapat nilai url dari request. Saatnya kita menentukan
logika routing url sesuai dengan ketentuan menggunakan if else.

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method, url } = request;

if(url === '/') {


// TODO 2: logika respons bila url bernilai '/'
} else if(url === '/about') {
// TODO 3: logika respons bila url bernilai '/about'
} else {
// TODO 1: logika respons bila url bukan '/' atau '/about'
}

/** Kode komentar disembunyikan agar lebih ringkas */


}

Nice! Coba lihat komentar TODO (yang harus dikerjakan) pada kode tersebut. Kita
akan selesaikan TODO sesuai urutan yang ada yah. Urutan tersebut sengaja disusun
dari yang paling mudah, lalu merangkak ke yang lebih sulit.

Blok else yang paling terakhir (TODO pertama) akan tereksekusi bila url bukan
bernilai ‘/’ atau ‘/about’. Berdasarkan ketentuan yang ada di atas, kita harus
merespons dengan pesan “Halaman tidak ditemukan!”. Yuk kita langsung saja tulis
responsnya.
const requestListener = (request, response) => {
response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method, url } = request;

if(url === '/') {


// TODO 2: logika respons bila url bernilai '/'
} else if(url === '/about') {
// TODO 3: logika respons bila url bernilai '/about'
} else {
response.end('<h1>Halaman tidak ditemukan!</h1>');
}

/** Kode komentar disembunyikan agar lebih ringkas */


}

Good! Mari kita coba dahulu perubahan yang ada. Simpan perubahan pada berkas
server.js; jalankan ulang server dengan perintah npm run start; dan silakan lakukan
permintaan ke alamat selain ‘/’ atau ‘/about’. Seharusnya, server akan merespons
sesuai dengan pesan yang sudah kita tetapkan.

curl -X GET http://localhost:5000/home


// output: <h1>Halaman tidak ditemukan!</h1>
curl -X GET http://localhost:5000/hello
// output: <h1>Halaman tidak ditemukan!</h1>
curl -X GET http://localhost:5000/test
// output: <h1>Halaman tidak ditemukan!</h1>

Mantap! Satu ketentuan sudah selesai.

URL: ‘/’
Method: GET
Mengembalikan “Ini adalah homepage”.
Method: <any> (selain GET)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: ‘/about’
Method: GET
Mengembalikan “Halo! Ini adalah halaman about”.
Method: POST (dengan melampirkan data name pada body)
Mengembalikan “Halo, <name>! Ini adalah halaman about”.
Method: <any> (selain GET dan POST)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: <any> (selain / dan /about)
Method: <any>
Mengembalikan “Halaman tidak ditemukan!”.

Sekarang, kita lanjut ke ketentuan lainnya. Mari selesaikan logika untuk url ‘/’
terlebih dahulu.

Berdasarkan ketentuan yang ada, url ‘/’ hanya dapat diakses menggunakan method GET
oleh client. Jika tidak, maka server akan mengembalikan pesan “Halaman tidak dapat
diakses dengan <any> request”.

<any> bernilai method yang digunakan client

if(url === '/') {


if(method === 'GET') {
// response bila client menggunakan GET
} else {
// response bila client tidak menggunakan GET
}
}

Lanjut, kita berikan respons sesuai ketentuan pada masing-masing blok if dan else.

if(url === '/') {


if(method === 'GET') {
response.end('<h1>Ini adalah homepage</h1>');
} else {
response.end(`<h1>Halaman tidak dapat diakses dengan ${method}
request</h1>`);
}
}

Setelah selesai, ayo coba lagi perubahan yang kita lakukan. Simpan perubahan pada
berkas server.js; jalankan ulang server dengan perintah npm run start; dan lakukan
permintaan ke alamat ‘/’ dengan method GET dan lainnya. Harusnya sudah berjalan
sesuai dengan ketentuan yah!

curl -X GET http://localhost:5000


// output: <h1>Ini adalah homepage</h1>
curl -X POST http://localhost:5000
// output: <h1>Halaman tidak dapat diakses dengan POST request</h1>
curl -X DELETE http://localhost:5000
// output: <h1>Halaman tidak dapat diakses dengan DELETE request</h1>

Nice Work! Satu ketentuan lain sudah selesai!

URL: ‘/’
Method: GET
Mengembalikan “Ini adalah homepage”.
Method: <any> (selain GET)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: ‘/about’
Method: GET
Mengembalikan “Halo! Ini adalah halaman about”.
Method: POST (dengan melampirkan data name pada body)
Mengembalikan “Halo, <name>! Ini adalah halaman about”.
Method: <any> (selain GET dan POST)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: <any> (selain / dan /about)
Method: <any>
Mengembalikan “Halaman tidak ditemukan!”.

Bagaimana, mudah bukan? Kita lanjut ke ketentuan terakhir yuk!

Berdasarkan ketentuan yang ada, halaman /about dapat diakses oleh client dengan
menggunakan method GET dan POST. Selain kedua method tersebut, server akan
mengembalikan pesan “Halaman tidak dapat diakses menggunakan <any> request”.

Jadi, tulislah logika if else seperti ini:

else if(url === '/about') {


if(method === 'GET') {
// respons bila client menggunakan GET
} else if(method === 'POST') {
// respons bila client menggunakan POST
} else {
// respons bila client tidak menggunakan GET ataupun POST
}
}

Pada blok else terakhir, berikan respons sesuai dengan ketentuan.

else if(url === '/about') {


if(method === 'GET') {
// respons bila client menggunakan GET
} else if(method === 'POST') {
// respons bila client menggunakan POST
} else {
response.end(`<h1>Halaman tidak dapat diakses menggunakan ${method}
request</h1>`);
}

Selanjutnya, lengkapi juga respons bila client menggunakan GET dan POST sesuai
dengan ketentuan.

Tips: Agar tidak menulis ulang seluruh kode stream, salinlah kode blok POST yang
diberikan komentar. Namun, sesuaikan nilai responsnya ya.

Setelah selesai kode pada blok ‘/about’ akan tampak seperti ini:

else if (url === '/about') {


if (method === 'GET') {
response.end('<h1>Halo! Ini adalah halaman about</h1>')
} else if (method === 'POST') {
let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
const {name} = JSON.parse(body);
response.end(`<h1>Halo, ${name}! Ini adalah halaman about</h1>`);
});
} else {
response.end(`<h1>Halaman tidak dapat diakses menggunakan ${method}
request</h1>`);
}
}

Sekarang kita coba yuk! Simpan perubahan pada berkas server.js; jalankan ulang
server dengan perintah npm run start; dan coba lakukan permintaan ke alamat
‘/about’ dengan method GET, POST, dan lainnya. Harusnya sudah berjalan sesuai
dengan ketentuan yah!

curl -X GET http://localhost:5000/about


// output: <h1>Halo! Ini adalah halaman about</h1>
curl -X POST -H "Content-Type: application/json" http://localhost:5000/about -d
"{\"name\": \"Dicoding\"}"
// output: <h1>Halo, Dicoding! Ini adalah halaman about</h1>
curl -X PUT http://localhost:5000/about
// output: <h1>Halaman tidak dapat diakses menggunakan PUT request</h1>
curl -X DELETE http://localhost:5000/about
// output: <h1>Halaman tidak dapat diakses menggunakan DELETE request</h1>

Well Done! Akhirnya seluruh ketentuan selesai kita terapkan!

URL: ‘/’
Method: GET
Mengembalikan “Ini adalah homepage”.
Method: <any> (selain GET)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: ‘/about’
Method: GET
Mengembalikan “Halo! Ini adalah halaman about”.
Method: POST (dengan melampirkan data name pada body)
Mengembalikan “Halo, <name>! Ini adalah halaman about”.
Method: <any> (selain GET dan POST)
Mengembalikan “Halaman tidak dapat diakses dengan <any> request”.
URL: <any> (selain / dan /about)
Method: <any>
Mengembalikan “Halaman tidak ditemukan!”.

Anda bisa merapikan kode saat ini dengan menghapus komentar kode yang sudah tidak
digunakan lagi. Sehingga, sekarang berkas server.js tampak seperti ini:

const http = require('http');

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.statusCode = 200;

const { method, url } = request;

if(url === '/') {


if(method === 'GET') {
response.end('<h1>Ini adalah homepage</h1>');
} else {
response.end(`<h1>Halaman tidak dapat diakses dengan ${method}
request</h1>`);
}
} else if(url === '/about') {
if(method === 'GET') {
response.end('<h1>Halo! Ini adalah halaman about</h1>')
} else if(method === 'POST') {
let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
const { name } = JSON.parse(body);
response.end(`<h1>Halo, ${name}! Ini adalah halaman about</h1>`);
});
} else {
response.end(`<h1>Halaman tidak dapat diakses menggunakan ${method}
request</h1>`);
}
} else {
response.end('<h1>Halaman tidak ditemukan!</h1>');
}
};

const server = http.createServer(requestListener);

const port = 5000;


const host = 'localhost';

server.listen(port, host, () => {


console.log(`Server berjalan pada http://${host}:${port}`);
});

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Response Status
Sejauh ini kita telah membahas banyak tentang request. Kita sudah mengenal dan
menggunakan method, url, body request, kemudian memberikan respons sesuai dengan
karakteristik request yang ada.

Meskipun kita sudah bisa membuat server merespons permintaan, tapi sebenarnya kita
belum belajar lebih dalam mengenai respons. Untuk itu, mari beranjak membahas lebih
detail mengenai parameter kedua dari fungsi request listener ini.

Seperti yang sudah Anda ketahui pada modul pengenalan back-end, respons yang dibawa
oleh server dibagi menjadi tiga bagian penting. Yang pertama status line, atau bisa
kita sebut response status; yang kedua response header; dan yang ketiga response
body. Kita bahas mulai dari response status dahulu yah.

Response status merupakan salah satu bagian dari respons yang berisikan tentang
informasi apakah sebuah request berhasil atau gagal dilakukan. Status yang
diberikan berupa kode (status code) dan pesan dari kode tersebut dalam bentuk teks
(status message).

Indikasi keberhasilan request client ditentukan oleh response status code yang
dikirim oleh server. Karena itu, tentu nilai status code tak bisa sembarang kita
tetapkan. Status code haruslah bernilai 3 digit angka dengan ketentuan berikut:

100-199 : informational responses.


200 - 299 : successful responses.
300-399 : redirect.
400-499 : client error.
500-599 : server errors.

Fokus terhadap poin yang ditebalkan yah karena poin itu akan sering digunakan.
Silakan eksplorasi lebih detail mengenai status code pada halaman MDN mengenai HTTP
Status.

Pada Node.js, penetapan nilai status code pada response dilakukan melalui properti
response.statusCode.

const requestListener = (request, response) => {


// memberitahu client bahwa request resource yang diminta tidak ada.
response.statusCode = 404;
};

Oh ya! Dari halaman MDN yang diberikan di atas, kita juga bisa melihat bahwa status
code selalu diiringi dengan status message. Contoh 200 Ok, 400 Bad Request, dan 404
Not Found. Melalui status message ini kita dan juga client bisa paham maksud dari
status kode.

Status message memiliki nilai standar sesuai dengan response code. Namun, kita bisa
mengubahnya bila diperlukan. Untuk mengubah status message, Anda bisa gunakan
properti response.statusMessage.

const requestListener = (request, response) => {


response.statusCode = 404;

// 404 defaultnya adalah 'not found'


response.statusMessage = 'User is not found';
};

Ketahuilah bahwa Anda sebaiknya tidak mengubah statusMessage dari nilai default
bila tidak diperlukan. Walaupun hanya sekadar menerjemahkannya menjadi “Tidak
ditemukan”.

Latihan Mengubah Response Code


Web server yang kita buat saat ini masih “cuek” dalam memberikan respons status
pada client. Maksudnya, apa pun respons yang diberikan server statusnya selalu 200
OK. Tidak percaya? Silakan lakukan request berikut pada curl.

curl -X GET http://localhost:5000/about -i

curl -X GET http://localhost:5000/test -i

curl -X DELETE http://localhost:5000/ -i

Hasilnya adalah:

Lihat informasi yang diberi warna kuning. Semua respons dari server berstatus 200
OK, ini membuat client mengira bahwa request berhasil dilakukan, tapi faktanya
tidak.

Ada beberapa respons yang seharusnya bisa diberikan status yang lebih relevan.
Seperti ketika client meminta resource yang tidak ditemukan (404 Not Found) atau
menggunakan method request yang tidak tepat (400 Bad Request).

Karena Anda saat ini sudah mengetahui cara mengubah status code pada respons, mari
kita perbaiki kesalahan ini.

Silakan buka kembali berkas server.js. Kemudian, hapus kode berikut dari fungsi
request listener:

response.statusCode = 200;

Sebagai gantinya, kita tuliskan nilai status code satu per satu sebelum perintah
response.end(). Tentu, sesuaikan nilai status code dengan kasus-kasus yang ada.

Contohnya, bila halaman tidak ditemukan, beri nilai 404 pada status code; bila
halaman tidak bisa diakses menggunakan method tertentu, beri nilai 400 pada status
code; sisanya, bila request berhasil dilakukan, beri nilai 200 pada status code.
Yuk kita eksekusi!

Setelah semuanya selesai, fungsi request listener tampak seperti ini:

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
const { method, url } = request;

if(url === '/') {


if(method === 'GET') {
response.statusCode = 200;
response.end('<h1>Ini adalah homepage</h1>');
} else {
response.statusCode = 400;
response.end(`<h1>Halaman tidak dapat diakses dengan ${method}
request</h1>`);
}
} else if(url === '/about') {
if(method === 'GET') {
response.statusCode = 200;
response.end('<h1>Halo! Ini adalah halaman about</h1>')
} else if(method === 'POST') {
let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
const { name } = JSON.parse(body);
response.statusCode = 200;
response.end(`<h1>Halo, ${name}! Ini adalah halaman about</h1>`);
});
} else {
response.statusCode = 400;
response.end(`<h1>Halaman tidak dapat diakses menggunakan ${method}
request</h1>`);
}
} else {
response.statusCode = 404;
response.end('<h1>Halaman tidak ditemukan!</h1>');
}
};

Simpan perubahan pada berkas server.js; jalankan ulang server dengan perintah npm
run start; dan coba lakukan lagi permintaan berikut menggunakan cURL:

curl -X GET http://localhost:5000/about -i

curl -X GET http://localhost:5000/test -i

curl -X DELETE http://localhost:5000/ -i

Sekarang server akan memberikan status respons yang sesuai.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Response Header
Pada web server yang sudah kita buat, ia memberikan respons dengan format dokumen
HTML. Dokumen ini digunakan oleh browser dalam menampilkan website. Anda bisa
melihat ini ketika mengakses web server melalui browser.
Pada url http://localhost:5000 server akan mengembalikan pesan “Ini adalah
homepage” atau pada url http://localhost:5000/about server akan mengembalikan pesan
“Halo! Ini adalah halaman about”. Pesan yang ditampilkan tampak besar dan tebal
karena ia dibungkus oleh elemen heading HTML.

Sebenarnya, server bisa merespons dengan memberikan data dalam tipe (MIME types)
lain, seperti XML, JSON, gambar, atau sekadar teks biasa. Apa pun MIME types yang
digunakan, web server wajib memberi tahu pada client.

Caranya, lampirkan property ‘Content-Type’ dengan nilai MIME types yang disisipkan
pada header response. Untuk menyisipkan nilai pada header response, gunakanlah
method setHeader().

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
};

Silakan eksplorasi apa saja MIME types yang bisa diberikan pada header Content-Type
di halaman Common Types dari MDN ini.

Anda bisa menetapkan data pada header sebanyak yang diinginkan. Method setHeader()
menerima dua parameter, yakni nama properti dan nilai untuk headernya.

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'text/html');
response.setHeader('X-Powered-By', 'NodeJS');
};

Jika Anda menetapkan header dengan properti yang tidak standar (lihat apa saja
standard properti pada header) atau Anda buat nama propertinya secara mandiri, maka
sangat disarankan untuk menambahkan huruf X di awal nama propertinya.

Ketahuilah juga bahwa penulisan properti header dituliskan secara Proper Case atau
setiap kata diawali dengan huruf kapital dan setiap katanya dipisahkan oleh tanda
garis (-).

Latihan mengubah dan menambah nilai header response


Mari kita latihan!

Saat ini web server yang kita buat menampilkan format HTML dalam mengirimkan
respons ke client. Nah, pada latihan kali ini kita akan mengubah format HTML
menjadi format JSON. Selain itu, kita akan tambahkan properti X-Powered-By pada
header untuk memberitahu client teknologi server apa yang kita gunakan.

Latihan ini akan sangat mudah. Jadi, yuk langsung saja!

Buka kembali berkas server.js dan lihat kode apa yang sekiranya perlu kita ubah
untuk memberi tahu server bahwa kita akan menggunakan JSON untuk responnya?

Jika Anda mengira kode ini:

response.setHeader('Content-Type', 'text/html');

Tebakan Anda tepat sekali!

Seperti yang sudah Anda ketahui, properti header ‘Content-Type’ berfungsi untuk
memberi tahu client seperti apa ia harus menampilkan data.
Contoh dengan nilai ‘text/html’, client khususnya browser akan menampilkan data
yang dikirim oleh respons akan di-render atau ditampilkan dalam bentuk HTML. Itulah
mengapa pesan respons tampak besar ketika melakukan request menggunakan browser.

Karena kita ingin mengubah Content-Type menjadi JSON, maka ubah text/html menjadi
application/json.

response.setHeader('Content-Type', 'application/json');

Selanjutnya, tetapkan juga nilai properti X-Powered-By dengan nilai “NodeJS”.

response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'NodeJS');

Simpan perubahan pada berkas server.js; jalankan ulang server dengan perintah npm
run start; dan coba lakukan lagi permintaan ke server menggunakan cURL.

Anda bisa lihat, sekarang nilai Content-Type berubah menjadi ‘application/json’.


Selain itu, ada properti header baru yang kita tetapkan, yakni X-Powered-By dengan
nilai ‘NodeJS’.

Oh ya, karena server tidak lagi mengirimkan konten dalam bentuk HTML, maka browser
tidak akan lagi menampilkan dalam bentuk HTML. Coba buka http://localhost:5000
melalui browser. Sekarang konten HTML tidak lagi ter-render.

Dengan begitu, memberikan pesan dalam format HTML sudah tidak relevan lagi. Kita
akan mengubahnya menjadi format JSON. Tapi sabar, kita tak akan lakukan itu
sekarang. Kita pelajari dahulu lebih dalam bagaimana cara mengirimkan body respons
pada server.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Response Body
Header respons menampung informasi terkait respons yang diberikan oleh server.
Informasi dapat berupa status respons, MIME types, tanggal, atau informasi lainnya
yang mungkin dibutuhkan oleh client.

Walaupun kita bisa memberikan informasi apa pun, namun tidak semua informasi cocok
disimpan di header. Informasi pada header hanya sebagai metadata atau informasi
yang menjelaskan tentang sebuah data lain (data utama).

Selain header, HTTP respons juga membawa body (Anda mengetahui ini pada materi pola
komunikasi client dan server). Di dalam body inilah data utama (atau bisa kita
sebut konten) seharusnya disimpan.

Ketahuilah bahwa objek response yang berada pada parameter fungsi request listener
adalah instance dari http.serverResponse. Di mana ia merupakan WritableStream.
Masih ingat cara menulis data pada WritableStream? Nah, cara itulah yang digunakan
untuk menuliskan data pada body response.

Seperti objek WritableStream lainnya, untuk menuliskan data pada respons kita bisa
gunakan method response.write() dan diakhiri dengan method response.end().

const requestListener = (request, response) => {


response.write('<html>');
response.write('<body>');
response.write('<h1>Hello, World!</h1>');
response.write('</body>');
response.write('</html>');
response.end();
};

Seperti yang sudah Anda ketahui juga, method end() pada WritableStream dapat
digunakan untuk menulis data terakhir sebelum proses penulisan diakhiri. Jadi,
untuk kasus di atas dapat dipersingkat penulisannya menjadi seperti ini.

const requestListener = (request, response) => {


response.end('<html><body><h1>Hello, World!</h1></body></html>');
};

Ketahuilah bahwa penting untuk menuliskan status dan header response sebelum Anda
menuliskan data pada body. Karena tidak masuk akal bila Anda sudah menuliskan body,
namun belum memberikan metadata terkait data apa yang hendak dikirim.

Latihan Mengubah Data pada Body Response


Di latihan sebelumnya Anda sudah mengubah properti Content-Type pada header menjadi
application/json. Namun untuk konten yang dikirim server pada body, masih berformat
HTML. Nah, pada latihan kali ini kita akan mengubah konten pada body menjadi format
JSON. Ayo kita eksekusi!

Pastikan Anda sudah tahu dan paham apa itu dan bagaimana penulisan JSON. Bila
tidak, silakan ulas kembali materi format request dan response.

Ketentuannya begini, setiap JSON yang akan kita kirimkan harus memiliki message.
Nilai properti message akan diisi dengan pesan yang sebelumnya kita berikan dalam
format HTML. Untuk lebih jelasnya, berikut contoh response body ketika client
meminta halaman yang tidak ditemukan.

{
"message": "Halaman tidak ditemukan!"
}

Sudah paham? Yuk langsung saja kita buka kembali server.js.

Kita ubah konten yang mudah dahulu yah, lebih tepatnya ketika client mengakses
halaman yang tidak ditemukan. Silakan ubah kode ini:

response.end('<h1>Halaman tidak ditemukan!</h1>');

Menjadi:

response.end(JSON.stringify({
message: 'Halaman tidak ditemukan!',
}));

Karena response.end() menerima string (atau buffer), maka kita perlu mengubah objek
JavaScript menjadi JSON string menggunakan JSON.stringify().

Mari kita coba dulu perubahan yang ada. Simpan perubahan pada berkas server.js;
jalankan ulang server dengan perintah npm run start; dan coba lakukan permintaan ke
alamat selain ‘/’ atau ‘/about’. Seharusnya, server akan merespons konten dengan
format JSON.

curl -X GET http://localhost:5000/anything


// output: { "message":"Halaman tidak ditemukan!"}
curl -X GET http://localhost:5000/test
// output: { "message":"Halaman tidak ditemukan!"}

Mantap! Silakan lanjut ubah format pesan untuk respons yang lain juga yah. Hingga
fungsi request listener pada server.js tampak seperti ini:

const requestListener = (request, response) => {


response.setHeader('Content-Type', 'application/json');
response.setHeader('X-Powered-By', 'NodeJS');

const { method, url } = request;

if(url === '/') {


if(method === 'GET') {
response.statusCode = 200;
response.end(JSON.stringify({
message: 'Ini adalah homepage',
}));
} else {
response.statusCode = 400;
response.end(JSON.stringify({
message: `Halaman tidak dapat diakses dengan ${method} request`,
}));
}
} else if(url === '/about') {
if(method === 'GET') {
response.statusCode = 200;
response.end(JSON.stringify({
message: 'Halo! Ini adalah halaman about',
}));
} else if(method === 'POST') {
let body = [];

request.on('data', (chunk) => {


body.push(chunk);
});

request.on('end', () => {
body = Buffer.concat(body).toString();
const { name } = JSON.parse(body);
response.statusCode = 200;
response.end(JSON.stringify({
message: `Halo, ${name}! Ini adalah halaman about`,
}));
});
} else {
response.statusCode = 400;
response.end(JSON.stringify({
message: `Halaman tidak dapat diakses menggunakan ${method},
request`
}));
}
} else {
response.statusCode = 404;
response.end(JSON.stringify({
message: 'Halaman tidak ditemukan!',
}));
}
};
Well done! Simpan perubahan pada berkas server.js; jalankan ulang server dengan
perintah npm run start; dan coba lakukan lagi permintaan ke server menggunakan
cURL. Server saat ini akan merespon dengan JSON sepenuhnya.

curl -X GET http://localhost:5000/


// output: {"message":"Ini adalah homepage"}
curl -X GET http://localhost:5000/about
// output: {"message":"Halo! ini adalah halaman about"}
curl -X DELETE http://localhost:5000/
// output: {"message":"Halaman tidak dapat diakses dengan DELETE request"}

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Node.js Web Framework


Selamat yah, Anda sudah bisa membuat web server sederhana menggunakan Node.js.
Dengan begitu, Anda diharapkan semakin mengerti dalam memahami bagaimana client dan
server berkomunikasi melalui HTTP.

Ketika membuat web server menggunakan Node.js, mungkin sebagian Anda bertanya-
tanya, “Apakah tidak ada cara yang lebih efektif lagi untuk membuat server
menggunakan Node.js? Apakah harus sesulit itu? Haruskah fungsi request listener
menampung seluruh logika? Bisakah membuat request handler secara spesifik
berdasarkan url atau method? Bisakah kita mengatur kode agar lebih efektif dan
mudah dibaca?” Wah, tenang, tenang!

Sejatinya memang seperti itulah dasar pembuatan web server menggunakan Node.js.
Node.js tidak menyediakan cara mudah bahkan untuk melakukan hal-hal umum yang
biasanya dilakukan ketika membuat web server.

Lantas, apakah cara yang sudah kita pelajari sejauh ini bisa digunakan untuk
pengembangan web server yang kompleks, seperti membangun REST API?

Tentu bisa, namun akan sulit. Sulit untuk dipelihara, sulit untuk dipahami, dan
juga sulit untuk dikembangkan. Tapi jangan berkecil hati dulu, bila mengalami
kesulitan, kita harus cari bala bantuan. Siapa yang bisa menolong kita saat ini?

Jawabannya adalah tentu developer lain! Masalah yang kita hadapi saat ini sudah
banyak dialami oleh Node.js developer lainnya.

Karena itu, baik developer, organisasi, atau bahkan instansi berlomba-lomba membuat
solusi dengan membangun framework yang dapat membantu membuat web server dengan
Node.js lebih cepat dan lebih mudah dikembangkan. Dengan bekal dasar yang sudah
dimiliki saat ini, Anda berhak untuk mengeksplorasi dan menggunakan framework yang
ada.

Tapi sabar dulu, sebelum menggunakannya, alangkah lebih baik kita pahami dahulu
lebih dalam apa itu Web Framework.

Apa itu Web Framework?


Web Framework adalah sebuah kerangka yang dapat membantu mempermudah pengembangan
web termasuk dalam membuat web server. Dengan menggunakan framework, penulisan kode
akan lebih terstruktur, mudah dipelihara, dan gampang dikembangkan.

Web Framework menyediakan sekumpulan tools dan library yang dapat menyederhanakan
hal-hal yang sering dilakukan dalam pengembangan web, seperti pembuatan server,
routing, menangani permintaan, interaksi dengan database, otorisasi, hingga
meningkatkan ketahanan web dari serangan luar.
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Web Framework di Node.js


Di Node.js terdapat banyak Web Framework yang dapat Anda gunakan. Masing-masing
framework yang ada tentu memiliki kelebihan dan kekurangan.

Expressjs merupakan web framework tertua dan terpopuler di Node.js saat ini.
Framework ini sangat ringan, mudah diintegrasikan dengan aplikasi web front-end,
dan penulisan kodenya tidak jauh beda dengan Node.js native.

Namun karena sifat ringannya tersebut, ia menjadi framework yang unopinionated


alias tidak memiliki aturan untuk menggunakannya. Express tidak menyediakan
struktur atau kerangka kerja yang baku untuk diikuti oleh developer. Sehingga,
developer menjadi sulit menentukan seperti apa kode yang optimal.

Framework lainnya seperti Hapi menyediakan environment yang lengkap untuk


mengembangkan web server yang kompleks. Bila menggunakan Hapi, kita tak perlu tools
lain untuk menerapkan layer authentication, tokenize, cors, dan lain sebagainya.

Kelemahan Hapi adalah abstraksinya yang terlalu jauh dari Node.js native. Kita
perlu belajar secara dalam, untuk menguasai framework ini.

Penggunaan framework menjadi pilihan personal. Salah satu faktornya adalah kasus
yang hendak Anda hadapi. Ketika ingin membangun server yang sederhana, katakanlah
untuk mendukung aplikasi front-end di-render di sisi server, express adalah pilihan
yang tepat.

Namun, bila Anda ingin membangun web server yang kompleks tanpa membutuhkan effort
yang besar, Hapi adalah pilihan yang tepat.

Kita akan membangun web server dengan arsitektur REST yang kompleks ke depannya.
Agar Anda selalu “Hapi” ketika mengikuti alur belajar, kita akan gunakan Hapi dalam
membangun web server.

Ketahuilah bahwa Hapi memiliki environment yang cukup luas. Kelas ini tidak akan
mengajarkan secara dalam tentang API yang ada di Hapi, melainkan hanya fitur-fitur
yang menjadi dasar pembuatan REST API. Jadi, bila Anda ingin mendalami terkait
framework Hapi, sempatkan waktu untuk eksplorasi di dokumentasi Hapi yang
disediakan yah.

Pada materi selanjutnya kita akan belajar dasar-dasar dari Hapi sambil coba membuat
ulang web server dengan spesifikasi yang sama seperti yang kita lakukan pada
latihan sebelumnya.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Membangun Web Server menggunakan Hapi

Menyiapkan Project
Mari kita awali dengan membuat proyek baru. Silakan buat folder di C -> javascript-
projects (Windows) atau home -> javascript-projects (Linux dan macOS) dengan nama
“hapi-web-server”.

Buka folder menggunakan VSCode, kemudian inisialisasi proyek pada Terminal dengan
menggunakan perintah:

npm init --y

Lanjut, kita atur NPM runner pada package.json menjadi seperti ini:

"scripts": {
"start": "node server.js"
},

Lalu, buatlah berkas JavaScript baru dengan nama server.js. Kemudian, tuliskan kode
berikut:

server.js
console.log('Halo, kita akan belajar membuat server menggunakan Hapi');

Simpan perubahan pada berkas server.js dan coba jalankan perintah berikut pada
Terminal:

npm run start

Bila Anda melihat pesan “Halo, kita akan belajar membuat server menggunakan Hapi”,
maka proyek telah siap digunakan.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Membuat HTTP Server


Untuk membuat HTTP server menggunakan Hapi, kita tidak lagi menggunakan core module
http secara langsung. Namun, kita akan membuat server melalui modul pihak ketiga
@hapi/hapi. Untuk menggunakan modul tersebut, kita perlu memasang terlebih dahulu
melalui NPM dengan perintah.

npm install @hapi/hapi

Setelah proses pemasangan berhasil, barulah kita bisa menggunakan modul tersebut.

const Hapi = require('@hapi/hapi');

Pembuatan server menggunakan Hapi memiliki struktur kode yang berbeda dari cara
asli. Berikut adalah dasar kode dalam membuat HTTP server pada Hapi:

const Hapi = require('@hapi/hapi');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
}

init();

Mari kita bedah kodenya.


HTTP server sendiri dibuat melalui method Hapi.server(). Method ini menerima satu
parameter yakni ServerOptions. ServerOptions merupakan objek yang menampung
konfigurasi dari server yang hendak dibuat, salah satunya kita bisa menetapkan
properti port dan host.

Proses menjalankan server (server.start()) dilakukan secara asynchronous. Karena


itu, kita perlu menjalankannya di dalam fungsi async dan memanggil server.start()
menggunakan await.

Setelah server berhasil berjalan, Anda bisa melihat alamat lengkap dan port di mana
server dijalankan melalui properti server.info.uri.

Latihan membuat HTTP Server


Ayo! Sekarang praktikan pada server hapi-web-server yang telah kita siapkan
sebelumnya.

Pertama, kita pasang dahulu modul @hapi/hapi dengan cara eksekusi perintah berikut
pada Terminal proyek:

npm install @hapi/hapi

Untuk memastikan modul @hapi/hapi berhasil terpasang, lihat berkas package.json.


Pastikan di sana terdapat properti dependencies dan menampung modul @hapi/hapi
beserta versi yang digunakan.

"dependencies": {
"@hapi/hapi": "^20.1.0"
}

Proses instalasi modul selesai! Kita lanjut ke penulisan kode pada berkas
server.js.

Silakan hapus kode yang ada pada server.js, lalu ganti dengan kode dasar dalam
pembuatan server menggunakan Hapi berikut ini:

const Hapi = require('@hapi/hapi');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

Simpan perubahan pada berkas server.js. Kemudian jalankan perintah npm run start
pada Terminal. Jika server berhasil dijalankan, maka Anda akan melihat pesan
‘Server berjalan pada http://localhost:5000’.

Silakan lakukan permintaan ke http://localhost:5000 melalui cURL. Perhatikan,


server akan merespons seperti ini:

Yups! Hapi secara default akan mengembalikan response “Not Found” ketika tidak ada
request handler yang menangani permintaannya. Hal ini tentu lebih baik daripada
permintaannya dibiarkan begitu saja, bukan?
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Method/Verb Request dan Routing


Setelah membuat dan menjalankan server, selanjutnya adalah menambahkan routing agar
server dapat merespons permintaan sesuai dengan method dan url yang diminta oleh
client.

Routing pada Hapi tidak dilakukan di dalam request handler seperti cara native.
Namun, ia memanfaatkan objek route configuration yang disimpan pada method
server.route(). Lihat kode yang ditebalkan yah.

const init = async () => {

const server = Hapi.server({


port: 5000,
host: 'localhost'
});

server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Hello World!';
}
});

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

Objek route configuration memiliki properti yang bisa dimanfaatkan untuk


menspesifikasikan route yang diinginkan. Termasuk menspesifikasikan method, path,
dan fungsi sebagai handler untuk menangani permintaan tersebut (request handler).

Tunggu, request handler dituliskan di dalam route configuration? Yap benar! Handler
pada Hapi dipisahkan berdasarkan route yang ada. Setiap spesifikasi route memiliki
handler-nya masing-masing. Dengan begitu, tentu kode akan lebih mudah dikelola.
Anda bisa mengatakan selamat tinggal pada if else yang bersarang.

Lalu, bagaimana cara menetapkan lebih dari satu route configuration dalam method
server.route()? Mudah! Sebenarnya, server.route() selain dapat menerima route
configuration, ia juga dapat menerima array dari route configuration. Jadi, Anda
bisa secara mudah menentukan banyak spesifikasi route dengan seperti ini:

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

server.route([
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Homepage';
},
},
{
method: 'GET',
path: '/about',
handler: (request, h) => {
return 'About Page';
},
},
]);

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

Kami merekomendasi untuk memisahkan seluruh routes configuration pada berkas


JavaScript berbeda. Dengan begitu, satu berkas JavaScript hanya memiliki satu
fungsi atau tanggung jawab saja (single responsibility principle).

routes.js:
const routes = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Homepage';
},
},
{
method: 'GET',
path: '/about',
handler: (request, h) => {
return 'About page';
},
},
];

module.exports = routes;

server.js:
const Hapi = require('@hapi/hapi');
const routes = require('./routes');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

server.route(routes);

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

const Hapi = require('@hapi/hapi');


const routes = require('./routes');
const init = async () => {
const server = Hapi.server({
port: 5000,
host: 'localhost',
});

server.route(routes);

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

Latihan Routing
Setelah mengetahui cara menspesifikasikan route pada Hapi, sekarang saatnya kita
terapkan apa yang sudah kita ketahui pada web server yang sudah dibuat sebelumnya.

Pada latihan kali ini, kita akan membuat routes configuration dengan ketentuan
berikut:

URL: ‘/’
Method: GET
Mengembalikan pesan “Homepage”.
Method: <any> (selain method GET)
Mengembalikan pesan “Halaman tidak dapat diakses dengan method tersebut”.
URL: ‘/about’
Method: GET
Mengembalikan pesan “About page”.
Method: <any> (selain method GET)
Mengembalikan pesan “Halaman tidak dapat diakses dengan method tersebut”.
URL: <any> (selain “/’ dan “/about”)
Method: <any>
Mengembalikan pesan “Halaman tidak ditemukan”.

Yuk mulai!

Agar kode lebih terkelompok, tulis route configuration pada berkas JavaScript
terpisah. Silakan buat berkas JavaScript baru pada proyek hapi-web-server dengan
nama “routes.js”. Kemudian, tuliskan kumpulan routes configuration dalam bentuk
array sesuai dengan ketentuan.

const routes = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Homepage';
},
},
{
method: '*',
path: '/',
handler: (request, h) => {
return 'Halaman tidak dapat diakses dengan method tersebut';
},
},
{
method: 'GET',
path: '/about',
handler: (request, h) => {
return 'About page';
},
},
{
method: '*',
path: '/about',
handler: (request, h) => {
return 'Halaman tidak dapat diakses dengan method';
},
},
{
method: '*',
path: '/{any*}',
handler: (request, h) => {
return 'Halaman tidak ditemukan';
},
},
];

module.exports = routes;

Tunggu, sepertinya ada beberapa hal baru yang belum Anda ketahui. Mari kita bedah
kode yang ditebalkan yah.

Anda bisa lihat beberapa properti method memiliki nilai ‘*’, itu artinya route
dapat diakses menggunakan seluruh method yang ada pada HTTP.

Kemudian nilai ‘/{any*}’ pada route paling akhir, ini berfungsi untuk menangani
permintaan masuk pada path yang belum Anda tentukan. Ini merupakan salah satu
teknik dalam menetapkan routing yang dinamis menggunakan Hapi.

Namun, routing dengan nilai dinamis seperti itu akan kalah kuatnya dengan nilai
yang ditetapkan secara spesifik. Contohnya bila array route configuration memiliki
nilai seperti ini:

const routes = [
{
method: '*',
path: '/',
handler: (request, h) => {
return 'Halaman tidak dapat diakses dengan method tersebut';
},
},
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Homepage';
},
},
];

Kemudian, client meminta request dengan spesifikasi berikut:

curl -X GET http://localhost:5000


Maka server akan mengembalikan “Homepage” karena route tersebut lebih spesifik.

Oke, sudah paham? Jika sudah, mari kita lanjutkan.

Setelah menetapkan nilai routes configuration, gunakan nilainya menggunakan method


server.route() pada berkas server.js. Lihat kode yang dihitamkan yah.

const Hapi = require('@hapi/hapi');


const routes = require('./routes');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

server.route(routes);

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

Simpan seluruh perubahan yang ada baik pada berkas routes.js dan server.js;
jalankan ulang server dengan perintah npm run start; dan coba lakukan permintaan ke
server. Seharusnya server sudah bisa merespons sesuai dengan yang diharapkan.

curl -X GET http://localhost:5000


// output: Homepage
curl -X GET http://localhost:5000/about
// output: About page
curl -X GET http://localhost:5000/test
// output: Halaman tidak ditemukan
curl -X POST http://localhost:5000
// output: Halaman tidak dapat diakses dengan method tersebut

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Path Parameter
Mari kita berbicara mengenai teknik routing lebih lanjut. Path dalam routing bisa
dikatakan sebagai alamat yang digunakan client untuk melakukan permintaan ke
server. Alamat atau path yang dibuat biasanya merupakan teks verbal yang dapat
dimengerti oleh client. Tak jarang hanya dengan membaca path dari sebuah tautan
kita langsung mengerti apa yang client minta kepada server.

Sebagai contoh, ketika membaca tautan GitHub ini


https://github.com/dicodingacademy, Anda pasti mengerti bahwa client ingin meminta
server untuk menampilkan profil github dari username “dicodingacademy”.

Contoh lain, dari alamat https://twitter.com/maudyayunda, coba Anda tebak kira-kira


apa yang diminta client ke server? Jika Anda berpikir client meminta profil twitter
kak Maudy Ayunda, yups, Anda tepat sekali!

Twitter dan Github menggunakan pendekatan yang sama dalam menampilkan halaman
profil. Mereka memanfaatkan username sebagai bagian dari path untuk melakukan
permintaan ke server. Terbayang tidak sih bagaimana mereka melakukannya? Di saat
mereka memiliki pengguna yang banyak, apakah mereka menetapkan route secara satu
per satu berdasarkan username untuk setiap penggunanya? Tentu tidak!

Untuk melakukan hal tersebut, Twitter dan Github menggunakan teknik path parameter.
Di Hapi Framework teknik tersebut sangat mudah untuk diterapkan. Cukup dengan
membungkus path dengan tanda { }. Sebagai contoh:

server.route({
method: 'GET',
path: '/users/{username}',
handler: (request, h) => {
const { username } = request.params;
return `Hello, ${username}!`;
},
});

Seperti yang Anda lihat di atas, pada properti path terdapat bagian path yang
ditulis {username}. Itu berarti, server memberikan bagian teks tersebut untuk
client manfaatkan sebagai parameter.

Nantinya parameter ini akan disimpan sebagai properti pada request.params yang
dimiliki handler dengan nama sesuai yang Anda tetapkan (username). Sebagai contoh,
bila Anda melakukan permintaan ke server dengan alamat ‘/users/harry’, maka server
akan menanggapi dengan ‘Hello, harry!’.

Pada contoh kode di atas, nilai path parameter wajib diisi oleh client. Bila client
mengabaikannya dengan melakukan permintaan pada alamat ‘/users’, maka server akan
mengalami eror.

Tapi tenang, pada Hapi Anda dapat membuat path parameter bersifat opsional. Caranya
dengan menambahkan tanda “?” di akhir nama parameternya. Berikut contoh yang sama
namun dengan implementasi opsional path parameter:

server.route({
method: 'GET',
path: '/users/{username?}',
handler: (request, h) => {
const { username = 'stranger' } = request.params;
return `Hello, ${username}!`;
},
});

Sekarang bila client meminta pada alamat ‘/users/dicoding’, server menanggapi


dengan ‘Hello, dicoding!’; dan bila client meminta hanya pada path ‘/users’, server
akan menanggapinya dengan ‘Hello, stranger!’.

Anda bisa menetapkan lebih dari satu path parameter. Namun, penting untuk Anda
ketahui bahwa optional path parameter hanya dapat digunakan di akhir bagian path
saja. Artinya, jika Anda menetapkan optional path di tengah-tengah path parameter
lain contohnya /{one?}/{two}, maka path ini dianggap tidak valid oleh Hapi.

Latihan Path Parameter


Sekarang Anda sudah tahu apa itu path parameter, saatnya kita coba praktikkan pada
web server yang sudah dibuat.

Pada latihan kali ini, kita akan membuat route baru dengan nilai path
/hello/{name?}. Bila client melampirkan nilai path parameter, server harus
mengembalikan dengan pesan “Hello, name!”. Namun bila tidak, server harus
mengembalikan dengan nilai
“Hello, stranger!”. Sudah paham? Yuk kita mulai!

Buka berkas routes.js dan tambahkan route baru seperti ini (lihat kode yang
ditebalkan).

const routes = [
{
method: 'GET',
path: '/',
handler: (request, h) => {
return 'Homepage';
},
},
{
method: '*',
path: '/',
handler: (request, h) => {
return 'Halaman tidak dapat diakses dengan method tersebut';
},
},
{
method: 'GET',
path: '/about',
handler: (request, h) => {
return 'About page';
},
},
{
method: '*',
path: '/about',
handler: (request, h) => {
return 'Halaman tidak dapat diakses dengan method';
},
},
{
method: 'GET',
path: '/hello/{name?}',
handler: (request, h) => {

}
},
{
method: '*',
path: '/{any*}',
handler: (request, h) => {
return 'Halaman tidak ditemukan';
},
},
];

module.exports = routes;

Di dalam handler, dapatkan nilai path parameter melalui properti request.params.


Kita manfaatkan object destructuring untuk mendapatkan nilainya. Jangan lupa
berikan nilai default “stranger”.

Lalu, kembalikan fungsi handler dengan pesan sesuai dengan ketentuan yah. Sehingga,
fungsi handler tampak seperti ini:
{
method: 'GET',
path: '/hello/{name?}',
handler: (request, h) => {
const { name = "stranger" } = request.params;
return `Hello, ${name}!`;
},
},

Simpan perubahan pada berkas routes.js; coba jalankan kembali server dengan
perintah npm run start; dan lakukanlah permintaan melalui curl atau browser pada
path /hello/dicoding dan /hello.

curl -X GET http://localhost:5000/hello/dicoding


// output: Hello, dicoding!
curl -X GET http://localhost:5000/hello
// output: Hello, stranger!

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Query Parameters
Selain path parameter, terdapat cara lain yang sering digunakan dalam mengirimkan
data melalui URL, yakni dengan query parameter. Teknik ini umum digunakan pada
permintaan yang membutuhkan kueri dari client, contohnya seperti pencarian dan
filter data.

Data yang dikirim melalui query memiliki format key=value. Contohnya:

localhost:5000?name=harry&location=bali

Contoh di atas memiliki dua query parameter. Yang pertama adalah name=harry dan
location=bali. Di Hapi, Anda bisa mendapatkan nilai dari query parameter melalui
request.query.

server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
const { name, location } = request.query;
return `Hello, ${name} from ${location}`;
},
});

Latihan Query Parameters


Saatnya latihan!

Pada latihan kali ini kita akan menambahkan dukungan bahasa terhadap path
/hello/{name} yang sudah kita buat. Bila path tersebut memiliki kueri lang dengan
nilai id, maka server akan menanggapi dengan pesan “Hai, {name}!”. Selain itu,
biarkan pesan tetap sama seperti latihan sebelumnya. Ayo kita mulai!

Buka berkas routes.js dan pada fungsi handler GET /hello/ {name} dapatkan nilai
kueri lang melalui properti request.query.

{
method: 'GET',
path: '/hello/{name?}',
handler: (request, h) => {
const { name = "stranger" } = request.params;
const { lang } = request.query;

return `Hello, ${name}!`;


},
},

Lalu, sesuaikan pesan kembalian handler berdasarkan evaluasi nilai lang seperti
ini:

{
method: 'GET',
path: '/hello/{name?}',
handler: (request, h) => {
const { name = "stranger" } = request.params;
const { lang } = request.query;

if(lang === 'id') {


return `Hai, ${name}!`;
}
return `Hello, ${name}!`;
},
},

Simpan perubahan pada berkas routes.js; jalankan kembali server dengan perintah npm
run start; dan lakukan permintaan pada path /hello/dicoding dengan dan tanpa
melampirkan kueri lang=id.

curl -X GET http://localhost:5000/hello/dicoding?lang=id


// output: Hai, dicoding!
curl -X GET http://localhost:5000/hello/dicoding
// output: Hello, dicoding!

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Body/Payload Request
Ketika menggunakan Node.js, untuk mendapatkan data pada body request--meskipun
datanya hanya sebatas teks--kita harus berurusan dengan Readable Stream. Di mana
untuk mendapatkan data melalui stream tak semudah seperti kita menginisialisasikan
sebuah nilai pada variabel.

Good News! Ketika menggunakan Hapi, Anda tidak lagi berurusan dengan stream untuk
mendapatkan datanya. Di balik layar, Hapi secara default akan mengubah payload JSON
menjadi objek JavaScript. Dengan begitu, Anda tak lagi berurusan dengan
JSON.parse()!

Kapan pun client mengirimkan payload berupa JSON, payload tersebut dapat diakses
pada route handler melalui properti request.payload. Contohnya seperti ini:

server.route({
method: 'POST',
path: '/login',
handler: (request, h) => {
const { username, password } = request.payload;
return `Welcome ${username}!`;
},
});

Pada contoh di atas, handler menerima payload melalui request.payload. Dalam kasus
tersebut, client mengirimkan data login dengan struktur:

{ "username": "harrypotter", "password": "encryptedpassword" }

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Response Toolkit
Fungsi handler pada Hapi memiliki dua parameters, yakni request dan h.

Sebagaimana yang sudah banyak kita bahas sebelumnya, request parameter merupakan
objek yang menampung detail dari permintaan client, seperti path dan query
parameters, payload, headers, dan sebagainya. Ada baiknya Anda eksplorasi secara
lebih dalam apa fungsi dari parameter request pada referensi API Hapi.

Parameter yang kedua yaitu h (huruf inisial Hapi). Parameter ini merupakan response
toolkit di mana ia adalah objek yang menampung banyak sekali method yang digunakan
untuk menanggapi sebuah permintaan client. Objek ini serupa dengan objek response
pada request handler ketika kita menggunakan Node.js native.

Seperti yang sudah Anda lihat pada contoh dan latihan sebelumnya, jika hanya ingin
mengembalikan nilai pada sebuah permintaan yang datang, di Hapi Anda bisa secara
langsung mengembalikan nilai dalam bentuk teks, teks HTML, JSON, steam, atau bahkan
promise.

server.route({
method: 'GET',
path: '/',
handler: (request, h) => {
return `Homepage`;
},
});

Jika kita dapat mengembalikan permintaan secara singkat, lalu apa fungsi dari h?
Kapan kita membutuhkannya?

Bila kasusnya sederhana seperti di atas, memang lebih baik Anda langsung kembalikan
dengan nilai secara eksplisit. Namun, ketahuilah bahwa dengan cara tersebut status
response selalu bernilai 200 OK. Ketika Anda butuh mengubah nilai status response,
di situlah Anda membutuhkan parameter h.

server.route({
method: 'POST',
path: '/user',
handler: (request, h) => {
return h.response('created').code(201);
},
});

Fungsi handler harus selalu mengembalikan sebuah nilai, bila Anda menggunakan h
ketika menangani permintaan, maka kembalikanlah dengan nilai h.response(). Anda
bisa lihat contoh kode di atas.

Parameter h tidak hanya berfungsi untuk menetapkan status kode respons. Melalui h,
Anda juga bisa menetapkan header response, content type, content length, dan masih
banyak lagi.

// Detailed notation
const handler = (request, h) => {
const response = h.response('success');
response.type('text/plain');
response.header('X-Custom', 'some-value');
return response;
};

// Chained notation
const handler = (request, h) => {
return h.response('success')
.type('text/plain')
.header('X-Custom', 'some-value');
};

Untuk mendalami mengenai respons toolkit, sangat direkomendasikan untuk eksplorasi


lebih detail pada halaman dokumentasi yang diberikan oleh Hapi langsung.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Build RESTful API with Hapi


Di materi sebelumnya Anda telah belajar dasar-dasar pembuatan web server, mulai
dari membuat server HTTP, menangani dan menanggapi permintaan, hingga teknik
routing. Anda juga sudah mencoba membuat serta mengembangkan web server menggunakan
framework Hapi. Dengan bekal pengetahuan dasar yang ada, sekarang kita bisa
melangkah untuk membuat web server yang lebih kompleks, dan membangun proyek dengan
skenario yang lebih nyata dalam membangun sistem aplikasi.

Masih ingat tentang arsitektur REST yang sudah dibahas pada modul pengenalan? Nah,
pada materi kali ini kita akan membuat RESTful API mulai dari awal. Pada akhirnya,
Anda diharapkan bisa membuat server dari aplikasi catatan sederhana seperti pada
video ini.

Catatan
Jika Anda akses aplikasi pada tautan di atas, aplikasi tersebut tidak akan
berfungsi karena belum ada implementasi dari sisi Back-End. Tugas kita adalah
membuat aplikasi Back-End dan membuat aplikasi catatan berfungsi dengan baik.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Menyiapkan Proyek
Seperti biasa, mari kita awali dengan membuat proyek baru. Sudah tahu kan caranya?
Jika belum, silakan ikuti langkah berikut:

Buat folder baru dengan nama “notes-app-back-end” pada C -> javascript-projects


(Windows) atau Home -> javascript-projects (Linux).
Buka folder tersebut menggunakan VSCode.
Buka Terminal proyek dan eksekusi perintah npm init --y.
Pastikan terdapat berkas package.json pada proyek Anda.

Voila! Proyek baru berhasil dibuat.


Sebelum membuat berkas JavaScript, kita akan gunakan dua tools tambahan untuk
memudahkan proses pengembangan web server. Jadi, mari kita siapkan kedua tools
tersebut. Apa saja?

Nodemon
Tools pertama adalah nodemon, ia bisa dikatakan wajib digunakan selama proses
pengembangan. Pasalnya, dengan tools ini kita tak perlu menjalankan ulang server
ketika terjadi perubahan pada berkas JavaScript. Nodemon akan mendeteksi perubahan
kode JavaScript dan mengeksekusi ulang secara otomatis.

Untuk menggunakannya, pasanglah package nodemon pada devDependencies dengan


mengeksekusi perintah berikut di Terminal proyek:

npm install nodemon --save-dev

Untuk memastikan nodemon terpasang pada proyek, Anda bisa memeriksa berkas
package.json, lebih tepatnya di objek devDependencies.

package.json
{
"name": "notes-app-back-end",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"nodemon": "^2.0.7"
}
}

Bila package berhasil terpasang, Anda bisa lihat properti nodemon dan versi yang
digunakan di sana.

Untuk mencoba nodemon, silakan buat berkas JavaScript dulu pada proyek kita dan
berikan nama “server.js”. Di dalamnya, tulislah kode berikut:

server.js
console.log('Hallo kita akan membuat RESTful API');

Kemudian di dalam package.json, buat npm runner script baru untuk menjalankan
server.js menggunakan nodemon.

"scripts": {
"start": "nodemon server.js"
},
Anda bisa menghapus runner script test karena saat ini tidak digunakan.

Lalu, jalankan perintah npm run start pada Terminal.

Nodemon berhasil mengeksekusi server.js dan akan terus mengawasi perubahan kode
yang ada. Yuhu! Kini Anda tidak perlu menjalankan ulang perintah npm run start
setiap terjadi perubahan pada berkas JavaScript. Cukup simpan perubahannya dan
nodemon akan menjalankan ulang secara otomatis.
-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

ESLint
Tools yang kedua adalah ESLint, ia dapat membantu atau membimbing Anda untuk selalu
menuliskan kode JavaScript dengan gaya yang konsisten. Seperti yang Anda tahu,
JavaScript tidak memiliki aturan yang baku untuk gaya penulisan kode, bahkan
penggunaan semicolon. Karena itu, terkadang kita jadi tidak konsisten dalam
menuliskannya.

ESLint dapat mengevaluasi kode yang dituliskan berdasarkan aturan yang Anda
terapkan. Anda bisa menuliskan aturannya secara mandiri atau menggunakan gaya
penulisan yang sudah ada seperti AirBnb JavaScript Code Style, Google JavaScript
Code Style, dan StandardJS Code Style. Kami sarankan Anda untuk mengikuti salah
satu code style yang ada. Mengapa begitu? Jawabannya karena code style tersebut
sudah banyak digunakan oleh JavaScript Developer di luar sana.

Untuk menggunakan ESLint, pasanglah package ESLint pada devDependencies proyek


Anda. Caranya, silakan eksekusi perintah berikut di Terminal:

npm install eslint --save-dev

Sama seperti package nodemon, setelah berhasil terpasang, package eslint akan
muncul di package.json lebih tepatnya pada devDependencies.

{
"name": "notes-app-back-end",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"start": "nodemon server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"eslint": "^7.19.0",
"nodemon": "^2.0.7"
}
}

Sebelum digunakan, Anda perlu melakukan konfigurasi terlebih dahulu. Caranya dengan
menggunakan perintah berikut di Terminal proyek.

npx eslint --init

Kemudian Anda akan diberikan beberapa pertanyaan, silakan jawab pertanyaan yang ada
dengan jawaban berikut:

How would you like to use ESLint? -> To check, find problems, and enforce code
style.
What type of modules does your project use? -> CommonJS (require/exports).
Which framework did you use? -> None of these.
Does your project use TypeScript? -> N.
Where does your code run? -> Node (pilih menggunakan spasi).
How would you like to define a style for your project? -> Use a popular style
guide.
Which style guide do you want to follow? -> (Anda bebas memilih, sebagai contoh
pilih AirBnB).
What format do you want your config file to be in? -> JSON.
Would you like to …… (seluruh pertanyaan selanjutnya) -> Y.

Setelah menjawab seluruh pertanyaan yang diberikan, maka akan terbentuk berkas
konfigurasi eslint dengan nama .eslintrc.json.

Di dalam berkas tersebut tertulis konfigurasi sesuai dengan jawaban pada


pertanyaan-pertanyaan yang diberikan.

Setelah membuat konfigurasi ESLint, selanjutnya kita gunakan ESLint untuk memeriksa
kode JavaScript yang ada pada proyek. Namun sebelum itu, kita perlu menambahkan npm
runner berikut di dalam berkas package.json:

"scripts": {
"start": "nodemon server.js",
"lint": "eslint ./"
},

Jalankan perintah npm run lint pada Terminal proyek, lalu perhatikan hasilnya.

Pada Terminal, kita dapat melihat terdapat eror dan warning (bila Anda menggunakan
AirBnB code style). Seperti inilah fungsi dari ESLint, ia akan memberi tahu alasan
dan letak kesalahan dalam penulisan kode. Tiap eror yang tampil, itu menandakan
adanya penulisan kode yang tidak sesuai dengan style guide yang sudah kita
tetapkan. Melalui ESLint ini, kita dapat mencari letak kesalahan secara akurat dan
cepat.

ESLint dapat diintegrasikan dengan berbagai text editor, termasuk VSCode. Untuk
mengaktifkan integrasi, Anda bisa menggunakan ekstensi ESLint untuk Visual Studio
Code. Bagaimana cara mengunduh dan memasangnya? Mudah saja, silakan pilih menu
extensions.

Kemudian, cari ekstensi dengan nama “ESLint”.

Tekan tombol “install” untuk memasang ESLint. Simpel ‘kan?

Sekarang, mari kita kembali ke berkas server.js, di sana Anda akan melihat tanda
kuning pada kode console.

Untuk pengguna Windows, ekstensi ESLint belum sepenuhnya diaktifkan. Anda perlu
mengizinkan ekstensi ESLint berjalan melalui icon ‘Lampu’ yang muncul ketika Anda
mengarahkan kursor ke kode console.

Tekan ikon lampu tersebut, kemudian pilih opsi ESLint: Manage Library Execution.

Catatan: Jika Manage Library Execution tidak muncul pada VSCode Anda, itu berarti
ESLint extensions sudah dapat digunakan. Anda bisa abaikan langkah tersebut.

Pilih “Allow Everywhere” pada pop-up yang muncul. Kemudian, tutup dan buka ulang
proyek menggunakan VSCode.

Kini ekstensi ESLint sudah berjalan dengan normal.

Penggunaan console dianggap sebuah warning ketika Anda menerapkan AirBnB code
style. ESLint membantu menyoroti hal tersebut.

Agar sinkron dengan gaya penulisan di ESLint, Anda bisa mengatur indentasi dan line
spacing di VSCode sesuai dengan style guide yang digunakan pada ESLint. Pengaturan
tersebut berada di bottom bar VSCode.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Kriteria Proyek
Seperti yang sudah Anda ketahui, kita akan membangun RESTful API untuk aplikasi
catatan sederhana. Di mana aplikasi tersebut berfungsi untuk menyimpan (create),
melihat (read), mengubah (update), dan menghapus (delete) catatan. Fungsionalitas
ini dikenal sebagai operasi CRUD.

Dari segi Front-End (client), kami telah membuat aplikasi web-nya. Kami juga telah
men-deploy aplikasi tersebut sehingga Anda dapat mengaksesnya melalui tautan ini:
http://notesapp-v1.dicodingacademy.com/.

Namun ketika Anda mengaksesnya, aplikasi tersebut belum bisa digunakan. Anda tidak
bisa melihat, dan menambahkan catatan apapun. Tetapi percayalah, aplikasi tersebut
akan berfungsi dengan baik ketika Anda sudah membuat RESTful API sesuai dengan
kriteria yang dibutuhkan. Lantas apa saja kriterianya?

Kriteria 1 - Web Server dapat menyimpan catatan


Kriteria pertama adalah web server dapat menyimpan catatan yang ditambahkan melalui
aplikasi web. Tenang, untuk memenuhi kriteria ini Anda tidak perlu menggunakan
database. Cukup simpan pada memory server dalam bentuk array JavaScript.

Berikut struktur dari objek catatan yang perlu disimpan oleh server:

{
id: string,
title: string,
createdAt: string,
updatedAt: string,
tags: array of string,
body: string,
},

Berikut contoh data nyatanya:

{
id: 'notes-V1StGXR8_Z5jdHi6B-myT',
title: 'Sejarah JavaScript',
createdAt: '2020-12-23T23:00:09.686Z',
updatedAt: '2020-12-23T23:00:09.686Z',
tags: ['NodeJS', 'JavaScript'],
body: 'JavaScript pertama kali dikembangkan oleh Brendan Eich dari Netscape di
bawah nama Mocha, yang nantinya namanya diganti menjadi LiveScript, dan akhirnya
menjadi JavaScript. Navigator sebelumnya telah mendukung Java untuk lebih bisa
dimanfaatkan para pemrogram yang non-Java.',
},

Agar web server dapat menyimpan catatan melalui aplikasi client, web server harus
menyediakan route dengan path ‘/notes’ dan method POST.

Dalam menyimpan atau menambahkan notes, client akan mengirimkan permintaan ke path
dan method tersebut dengan membawa data JSON berikut pada request body:

{
"title": "Judul Catatan",
"tags": ["Tag 1", "Tag 2"],
"body": "Konten catatan"
}

Untuk properti id, createdAt, dan updatedAt harus diolah di sisi server, jadi
client tidak akan mengirimkan itu. Server harus memastikan properti id selalu unik.

Jika permintaan client berhasil dilakukan, respons dari server harus memiliki
status code 201 (created) dan mengembalikan data dalam bentuk JSON dengan format
berikut:

{
"status": "success",
"message": "Catatan berhasil ditambahkan",
"data": {
"noteId": "V09YExygSUYogwWJ"
}
}

Nilai dari properti noteId diambil dari properti id yang dibuat secara unik.

Bila permintaan gagal dilakukan, berikan status code 500 dan kembalikan dengan data
JSON dengan format berikut:

{
"status": "error",
"message": "Catatan gagal untuk ditambahkan"
}

Kriteria 2 - Web Server dapat menampilkan catatan


Kriteria selanjutnya adalah web server dapat menampilkan catatan. Kriteria ini
mengharuskan web server untuk mengirimkan seluruh atau secara spesifik data notes
yang disimpan.

Ketika client melakukan permintaan pada path ‘/notes’ dengan method ‘GET’, maka
server harus mengembalikan status code 200 (ok) serta seluruh data notes dalam
bentuk array menggunakan JSON. Contohnya seperti ini:

{
"status": "success",
"data": {
"notes": [
{
"id":"notes-V1StGXR8_Z5jdHi6B-myT",
"title":"Catatan 1",
"createdAt":"2020-12-23T23:00:09.686Z",
"updatedAt":"2020-12-23T23:00:09.686Z",
"tags":[
"Tag 1",
"Tag 2"
],
"body":"Isi dari catatan 1"
},
{
"id":"notes-V1StGXR8_98apmLk3mm1",
"title":"Catatan 2",
"createdAt":"2020-12-23T23:00:09.686Z",
"updatedAt":"2020-12-23T23:00:09.686Z",
"tags":[
"Tag 1",
"Tag 2"
],
"body":"Isi dari catatan 2"
}
]
}
}

Jika belum ada catatan satu pun pada array, server bisa mengembalikan data notes
dengan nilai array kosong seperti ini:

{
"status": "success",
"data": {
"notes": []
}
}

Selain itu, client juga bisa melakukan permintaan untuk mendapatkan catatan secara
spesifik menggunakan id melalui path ‘/notes/{id}’ dengan method ‘GET’. Server
harus mengembalikan status code 200 (ok) serta nilai satu objek catatan dalam
bentuk JSON seperti berikut:

{
"status": "success",
"data": {
"note": {
"id":"notes-V1StGXR8_Z5jdHi6B-myT",
"title":"Catatan 1",
"createdAt":"2020-12-23T23:00:09.686Z",
"updatedAt":"2020-12-23T23:00:09.686Z",
"tags":[
"Tag 1",
"Tag 2"
],
"body":"Isi dari catatan 1"
}
}
}

Bila client melampirkan id catatan yang tidak ditemukan, server harus merespons
dengan status code 404, dan data dalam bentuk JSON seperti ini:

{
"status": "fail",
"message": "Catatan tidak ditemukan"
}

Kriteria 3 - Web Server dapat mengubah catatan


Kriteria ketiga adalah web server harus dapat mengubah catatan. Perubahan yang
dimaksud bisa berupa judul, isi, ataupun tag catatan. Ketika client meminta
perubahan catatan, ia akan membuat permintaan ke path ‘/notes/{id}’, menggunakan
method ‘PUT’, serta membawa data JSON pada body request yang merupakan data catatan
terbaru.

{
"title":"Judul Catatan Revisi",
"tags":[
"Tag 1",
"Tag 2"
],
"body":"Konten catatan"
}

Jika perubahan data berhasil dilakukan, server harus menanggapi dengan status code
200 (ok) dan membawa data JSON objek berikut pada body respons.

{
"status": "success",
"message": "Catatan berhasil diperbaharui"
}

Perubahan data catatan harus disimpan ke catatan yang sesuai dengan id yang
digunakan pada path parameter. Bila id catatan tidak ditemukan, maka server harus
merespons dengan status code 404 (not found) dan data JSON seperti ini:

{
"status": "fail",
"message": "Gagal memperbarui catatan. Id catatan tidak ditemukan"
}

Kriteria 4 - Web Server dapat menghapus catatan


Kriteria terakhir adalah web server harus bisa menghapus catatan. Untuk menghapus
catatan, client akan membuat permintaan pada path ‘/notes/{id}’ dengan method
‘DELETE’. Ketika permintaan tersebut berhasil, maka server harus mengembalikan
status code 200 (ok) serta data JSON berikut:

{
"status": "success",
"message": "Catatan berhasil dihapus"
}

Catatan yang dihapus harus sesuai dengan id catatan yang digunakan client pada path
parameter. Bila id catatan tidak ditemukan, maka server harus mengembalikan respons
dengan status code 404 dan membawa data JSON berikut:

{
"status": "fail",
"message": "Catatan gagal dihapus. Id catatan tidak ditemukan"
}

-- akhir dari penjelasan kriteria

Bagaimana, sudah jelas? Itulah kriteria yang perlu dipenuhi oleh kita dalam
mengembangkan RESTful API nanti. Untuk memastikan apakah web server yang dibuat
sudah bekerja sesuai dengan kriteria, Anda perlu mencoba menggunakan aplikasi
client yang dihubungkan dengan web server. Bagaimana caranya? Kita akan bahas itu
nanti yah.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Struktur Proyek
Sebelum praktik langsung, ada baiknya untuk menyusun struktur proyek terlebih
dahulu agar pengembangan mudah dilakukan.
Pada pengembangan web server kali ini, kita tidak ingin semua kode dituliskan dalam
satu berkas saja sebab itu akan membuat kode menjadi semrawut, susah dibaca,
apalagi dipelihara. Karena Anda sudah belajar teknik modularisasi pada Node.js,
tentu tak ada masalah untuk memisahkan kode JavaScript menjadi beberapa berkas.

Kami memegang prinsip single responsibility approach. Artinya, kita gunakan satu
berkas JavaScript untuk satu tujuan saja. Nah, di proyek kali ini, kita akan
membuat setidaknya empat buah berkas JavaScript. Apa saja berkas dan kode yang
dituliskan di dalamnya? Mari kita rincikan.

server.js : Memuat kode untuk membuat, mengonfigurasi, dan menjalankan server HTTP
menggunakan Hapi.
routes.js : Memuat kode konfigurasi routing server seperti menentukan path, method,
dan handler yang digunakan.
handler.js : Memuat seluruh fungsi-fungsi handler yang digunakan pada berkas
routes.
notes.js : Memuat data notes yang disimpan dalam bentuk array objek.

Semua berkas JavaScript yang kita buat akan disimpan di dalam folder src. Hal ini
bertujuan agar terpisah dari berkas konfigurasi proyek seperti .eslintrc.json,
package.json, package-lock.json, dan node_modules.

Jadi secara keseluruhan struktur proyek akan tampak seperti ini:

notes-app-back-end
├── node_modules
├── src
│ ├── handler.js
│ ├── notes.js
│ ├── routes.js
│ └── server.js
├── .eslintrc.json
├── package-lock.json
└── package.json

Yuk, kita langsung buat saja folder src beserta berkas JavaScript yang dibutuhkan
di dalamnya. Untuk berkas server.js, Anda tidak perlu membuat baru, cukup pindahkan
berkas lama ke dalam folder src ya.

Setelah itu, struktur proyek kita sudah sesuai yah.

Karena berkas server.js sekarang berada di dalam folder src, jangan lupa ubah
alamat berkas tersebut pada npm runner script di berkas package.json. Silakan buka
berkas tersebut dan sesuaikan nilai di dalam scripts menjadi seperti ini:

"scripts": {
"start": "nodemon ./src/server.js",
"lint": "eslint ./src"
},

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Membuat HTTP Server


Mari kita mulai dengan membuat HTTP server menggunakan Hapi framework.

Silakan pasang package @hapi/hapi dengan mengeksekusi perintah berikut pada


Terminal proyek:

npm install @hapi/hapi

Lanjut, buka berkas server.js dan ganti kode yang ada dengan kode dalam membuat
server menggunakan Hapi seperti pada latihan sebelumnya.

const Hapi = require('@hapi/hapi');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

Catatan: Untuk pengguna MacOS, kami menyarankan untuk tidak menggunakan port 5000,
karena versi MacOS Monterey, port tersebut sudah digunakan atau dipesan untuk
layanan AirPlay Receiver. Anda bisa mengubah nilai port yang aman untuk digunakan
(Contoh, > 8000).

Anda sudah familier dengan kodenya kan? Silakan simpan perubahan kode pada berkas
server.js, lalu jalankan server dengan nodemon melalui perintah npm run start.

Biarkan nodemon tetap berjalan agar bila terjadi perubahan kode, kita tidak perlu
menjalankan ulang server.

Silakan buka browser dan coba akses url http://localhost:5000. Jika pada browser
tampak seperti ini:

Itu berarti server HTTP sudah terbuat dan berjalan.

Sampai di sini Anda sudah bisa menghubungkan alamat localhost:5000 (web server)
dengan aplikasi client. Silakan pilih “Change URL”.

Lalu, isi dengan host beserta port dari web server yang Anda buat. Contohnya
“localhost:5000”

Setelah Anda melihat URL dari web server, maka web server dan aplikasi client sudah
terhubung.

Meskipun sudah terhubung, tapi halaman masih menampilkan “Error displaying notes!
Make sure you have done with the back-end or correct url.” Hal itu wajar karena
kita belum melakukan apa pun terhadap web server yang kita buat.

Jika Anda menggunakan ESLint, ada satu hal yang perlu diperhatikan. Bila ada
warning atau error yang diberikan oleh ESLint namun hal itu tidak Anda setujui atau
ingin Anda hiraukan, maka Anda bisa menonaktifkan warning atau eror tersebut.

Contohnya, bila Anda menggunakan code style AirBnB, maka penggunaan console akan
dianggap warning.
Anda bisa menonaktifkan aturan no-console pada berkas .eslintrc.json dengan
menambahkan properti no-console dengan nilai off pada rules.

{
"env": {
"commonjs": true,
"es2021": true,
"node": true
},
"extends": [
"airbnb-base"
],
"parserOptions": {
"ecmaVersion": 12
},
"rules": {
"no-console": "off"
}
}

Dengan begitu, warning dari ESLint akan hilang untuk penggunaan console.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Menyimpan Catatan
Mari kita selesaikan kriteria satu per satu. Kriteria pertama adalah web server
harus bisa menyimpan catatan yang ditambahkan dari aplikasi client. Untuk
detailnya, tentu Anda sudah tahu kan?

Saat ini, aplikasi client belum bisa menambahkan catatan. Anda bisa coba sendiri
melalui tombol “Add note” di pojok kiri bawah halaman. Ketika Anda hendak
menambahkan catatan, browser akan menampilkan pesan seperti gambar di bawah ini.

Tugas kita saat ini adalah membuat fungsi menyimpan catatan bisa berjalan dengan
baik. Yuk langsung saja.

Dari kriteria yang sudah kita ketahui sebelumnya, agar web server dapat menyimpan
catatan, ia perlu menyediakan route dengan path ‘/notes’ dan method ‘POST’. Karena
itu, ayo kita langsung saja buat route-nya.

Silakan buka berkas routes.js dan tuliskan kode route pertama kita sesuai dengan
ketentuan.

routes.js
const routes = [
{
method: 'POST',
path: '/notes',
handler: () => {},
},
];

module.exports = routes;

Untuk fungsi handler, kita akan membuatnya pada berkas yang terpisah. Untuk
sekarang, isi dulu dengan nilai fungsi kosong seperti itu.
Jangan lupa untuk menuliskan module.exports = routes, agar routes dapat digunakan
oleh berkas server.js nantinya.

Sebelum menuliskan fungsi handler, mari kita buat dulu array untuk menampung objek
catatan pada berkas notes.js. Tulislah kode berikut:

notes.js
const notes = [];

module.exports = notes;

Jangan lupa untuk ekspor juga nilainya.

Lanjut kita buat fungsi handler untuk route ini. Silakan buka berkas handler.js dan
buat fungsi dengan nama “addNoteHandler”.

const addNoteHandler = (request, h) => {

};

Masih ingatkan bahwa fungsi handler pada Hapi memiliki dua parameters? Jadi, jangan
lupa untuk menambahkan parameter tersebut setiap kali membuat fungsi handler.

Lalu untuk mengekspor fungsi handler ini, kita gunakan objek literals yah. Ini
bertujuan untuk memudahkan ekspor lebih dari satu nilai pada satu berkas
JavaScript.

const addNoteHandler = (request, h) => {

};

module.exports = { addNoteHandler };

Langkah selanjutnya, mari kita tuliskan logika untuk menyimpan catatan dari client
ke dalam array notes.

Client mengirim data catatan (title, tags, dan body) yang akan disimpan dalam
bentuk JSON melalui body request. Masih ingatkan cara mendapatkan body request di
Hapi? Yap! Menggunakan properti request.payload. Yuk mari kita ambil datanya.

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;
};

Selain itu, objek notes yang perlu kita simpan harus memiliki struktur seperti ini:

{
id: string,
title: string,
createdAt: string,
updatedAt: string,
tags: array of string,
body: string,
},

Kita hanya mendapatkan nilai title, tags, dan body dari client, itu berarti sisanya
kita perlu olah sendiri. Mari kita pikirkan dari properti id dulu.
Kriteria menyebutkan, properti id merupakan string dan harus unik, kita akan
menggunakan bantuan library pihak ketiga untuk menghasilkan nilainya. nanoid
merupakan salah satu library yang populer untuk menangani ini. Jadi, silakan pasang
library tersebut dengan perintah.

npm install [email protected]

Catatan: Pastikan Anda memasang nanoid dengan versi 3.x.x. Karena jika menggunakan
versi terbaru, nanoid tidak dapat digunakan dengan format module CommonJS.

Untuk menggunakannya cukup mudah, kita hanya perlu memanggil method nanoid() dan
memberikan parameter number yang merupakan ukuran dari string-nya.

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
};

Jangan lupa untuk import nanoid dari package-nya.

const { nanoid } = require('nanoid');

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
};

Nice! Sekarang kita sudah memiliki nilai untuk properti id.

Selanjutnya properti createdAt dan updatedAt. Karena kasus sekarang adalah


menambahkan catatan baru, maka nilai kedua properti tersebut seharusnya sama. Jadi,
kita bisa secara mudah memberikan nilai new Date().toISOString();.

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
const createdAt = new Date().toISOString();
const updatedAt = createdAt;
};

Kita sudah memiliki properti dari objek catatan secara lengkap. Sekarang, saatnya
kita masukan nilai-nilai tersebut ke dalam array notes menggunakan method push().

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
const createdAt = new Date().toISOString();
const updatedAt = createdAt;

const newNote = {
title, tags, body, id, createdAt, updatedAt,
};
notes.push(newNote);
};
Jangan lupa impor array notes pada berkas handler.js.

const { nanoid } = require('nanoid');


const notes = require('./notes');

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
const createdAt = new Date().toISOString();
const updatedAt = createdAt;

const newNote = {
title, tags, body, id, createdAt, updatedAt,
};

notes.push(newNote);
};

Lalu, bagaimana menentukan apakah newNote sudah masuk ke dalam array notes? Mudah
saja! Kita bisa memanfaatkan method filter() berdasarkan id catatan untuk
mengetahuinya. Kurang lebih implementasinya seperti ini:

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
const createdAt = new Date().toISOString();
const updatedAt = createdAt;

const newNote = {
title, tags, body, id, createdAt, updatedAt,
};

notes.push(newNote);

const isSuccess = notes.filter((note) => note.id === id).length > 0;


};

Kemudian, kita gunakan isSuccess untuk menentukan respons yang diberikan server.
Jika isSuccess bernilai true, maka beri respons berhasil. Jika false, maka beri
respons gagal.

const addNoteHandler = (request, h) => {


const { title, tags, body } = request.payload;

const id = nanoid(16);
const createdAt = new Date().toISOString();
const updatedAt = createdAt;

const newNote = {
title, tags, body, id, createdAt, updatedAt,
};

notes.push(newNote);

const isSuccess = notes.filter((note) => note.id === id).length > 0;

if (isSuccess) {
const response = h.response({
status: 'success',
message: 'Catatan berhasil ditambahkan',
data: {
noteId: id,
},
});
response.code(201);
return response;
}
const response = h.response({
status: 'fail',
message: 'Catatan gagal ditambahkan',
});
response.code(500);
return response;
};

Fungsi handler selesai! Huft, panjang juga yah untuk menyelesaikan kriteria
pertama. Eits! Ini belum berakhir, perjalanan kita masih cukup jauh. Kita harus
tetap semangat!

Selanjutnya, mari kita gunakan fungsi handler ini pada konfigurasi route-nya.
Silakan buka routes.js, lalu ganti fungsi handler menjadi seperti ini:

{
method: 'POST',
path: '/notes',
handler: addNoteHandler,
},

Jangan lupa import fungsi addNoteHandler-nya pada berkas routes.js.

const { addNoteHandler } = require('./handler');

Setelah itu, mari gunakan route configuration pada server. Silakan buka berkas
server.js, kemudian tambahkan kode yang diberi tanda tebal yah.

const Hapi = require('@hapi/hapi');


const routes = require('./routes');

const init = async () => {


const server = Hapi.server({
port: 5000,
host: 'localhost',
});

server.route(routes);

await server.start();
console.log(`Server berjalan pada ${server.info.uri}`);
};

init();

Terakhir, simpan seluruh perubahan pada semua berkas JavaScript yang ada. Kemudian,
coba kembali akses fitur tambah catatan pada aplikasi client. Apakah sekarang sudah
berfungsi?
Wah! Ternyata masih saja eror. Ketahuilah pada web apps untuk mengetahui penyebab
eror terjadi, kita bisa melihatnya melalui browser console. Silakan buka browser
console dengan menekan CTRL + SHIFT + I dan Anda akan melihat pesan eror di sana.

Oh ternyata permintaan client gagal dilakukan karena dihalangi oleh same-origin


policy. Apa itu? Bagaimana cara menanganinya? Hmm, sabar yah! Kita hentikan sejenak
praktik ini dan mari bahas same-origin policy terlebih dahulu.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Same-Origin Policy
Server dapat menampung sebuah website, aplikasi, gambar, video, dan masih banyak
lagi. Ketika server menampung website, mungkin beberapa data gambar, video,
stylesheet biasanya diambil dari alamat server lain atau origin yang berbeda.
Contohnya stylesheet yang diambil dari Bootstrap CDN ataupun gambar yang diperoleh
dari server Unsplash. Hal ini wajar dan biasa dilakukan.

Namun apakah Anda tahu bahwa tidak semua data bisa diambil dari origin yang
berbeda? Contohnya data JSON yang didapatkan melalui teknik XMLHTTPRequest atau
fetch. Jika website meminta sesuatu menggunakan teknik tersebut dari luar origin-
nya, maka permintaan tersebut akan ditolak. Itu disebabkan oleh kebijakan same-
origin. Kasus ini terjadi pada aplikasi client dan web server yang kita buat.

Origin terdiri dari tiga hal: protokol, host, dan port number. Origin dari aplikasi
client kita adalah

http://notesapp-v1.dicodingacademy.com

Di mana protokolnya adalah http://, host-nya adalah notesapp-


v1.dicodingacademy.com, dan port-nya adalah :80 (implisit).

Selama aplikasi client mengakses data dari origin yang sama, hal itu dapat
dilakukan. Namun bila ada salah satu saja yang berbeda contohnya port 8001, maka
permintaan itu akan ditolak.

Dengan begitu jelas yah, apa penyebab gagalnya aplikasi client ketika melakukan
permintaan ke web server yang kita buat. Sudah jelas keduanya memiliki origin yang
berbeda. Origin web server kita saat ini adalah http://localhost:5000/

Lalu, apa solusi agar keduanya dapat berinteraksi? Tenang, untungnya ada mekanisme
yang dapat membuat mereka saling berinteraksi. Mekanisme tersebut disebut Cross-
origin resource sharing (CORS). Pertanyaannya, bagaimana cara menerapkannya?

Cukup mudah! Pada web server, kita hanya perlu memberikan nilai header ‘Access-
Control-Allow-Origin’ dengan nilai origin luar yang akan mengkonsumsi datanya
(aplikasi client).

const response = h.response({ error: false, message: 'Catatan berhasil ditambahkan'


});

response.header('Access-Control-Allow-Origin', 'http://notesapp-
v1.dicodingacademy.com');

return response;

Atau Anda bisa menggunakan tanda * pada nilai origin untuk memperbolehkan data
dikonsumsi oleh seluruh origin.
const response = h.response({ error: false, message: 'Catatan berhasil ditambahkan'
});

response.header('Access-Control-Allow-Origin', '*');

return response;

Cukup mudah kan?

Good news! Penerapannya akan jauh lebih mudah bila Anda menggunakan Hapi. Dengan
Hapi, CORS dapat ditetapkan pada spesifik route dengan menambahkan properti
options.cors di konfigurasi route. Contohnya seperti ini:

{
method: 'POST',
path: '/notes',
handler: addNoteHandler,
options: {
cors: {
origin: ['*'],
},
},
},

Bila ingin cakupannya lebih luas alias CORS diaktifkan di seluruh route yang ada di
server, Anda bisa tetapkan CORS pada konfigurasi ketika hendak membuat server
dengan menambahkan properti routes.cors. Contohnya seperti ini:

const server = Hapi.server({


port: 5000,
host: 'localhost',
routes: {
cors: {
origin: ['*'],
},
},
});

Sudah cukup jelas? Kalo begitu, ayo kita terapkan CORS pada web server kita.

Menerapkan CORS pada Web Server


Di modul ini, kita akan terapkan CORS pada cakupan server agar tak perlu lagi
repot-repot mendefinisikan CORS pada setiap route yang ada.

Silakan buka berkas server.js, lalu tambahkan CORS pada konfigurasi pembuatan
server seperti yang sudah Anda pelajari.

const server = Hapi.server({


port: 5000,
host: 'localhost',
routes: {
cors: {
origin: ['*'],
},
},
});

Simpan perubahan berkas server.js, pastikan server masih berjalan, dan silakan coba
masukan kembali catatan menggunakan aplikasi client. Percayalah, sekarang harusnya
bisa berjalan dengan baik.

Jika setelah memasukan catatan Anda dikembalikan ke halaman utama tanpa peringatan
apa pun, itu artinya Anda berhasil menambahkan catatan. Selamat yah!

Tapi sayang sekali, walaupun berhasil tersimpan, catatan tersebut masih belum dapat
kita lihat. Agar catatan dapat kita lihat, ayo kita selesaikan kriteria kedua!

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Menampilkan Catatan
Kita beranjak ke kriteria kedua, yakni menampilkan seluruh atau secara spesifik
catatan yang disimpan pada server. Sepertinya kriteria ini akan lebih mudah dari
kriteria sebelumnya. Kalau begitu kita langsung saja yah.

Pertama, kita buat konfigurasi route terlebih dahulu pada berkas routes.js.
Tetapkan path dengan nilai ‘/notes’ dan method dengan nilai ‘GET’. Untuk handler,
kita berikan dulu fungsi kosong.

const routes = [
{
method: 'POST',
path: '/notes',
handler: addNoteHandler,
},
{
method: 'GET',
path: '/notes',
handler: () => {},
},
];

Lanjut kita buat fungsi handler-nya pada berkas handler.js. Buat fungsi dengan nama
getAllNotesHandler dan kembalikan data dengan nilai notes di dalamnya.

const getAllNotesHandler = () => ({


status: 'success',
data: {
notes,
},
});

Yap! Semudah itu untuk handler mendapatkan seluruh catatan. Anda juga tidak perlu
menuliskan parameter request dan h karena ia tidak digunakan.

Jangan lupa untuk ekspor nilai getAllNotesHandler agar dapat digunakan di routes.js

const getAllNotesHandler = () => ({


status: 'success',
data: {
notes,
},
});

module.exports = { addNoteHandler, getAllNotesHandler };


Kembali ke berkas routes.js. Gunakan fungsi handler tersebut pada konfigurasi
route.

{
method: 'GET',
path: '/notes',
handler: getAllNotesHandler,
},

Jangan lupa untuk mengimpornya yah.

const { addNoteHandler, getAllNotesHandler } = require('./handler');

Simpan seluruh perubahan yang ada, dan coba kembali buka aplikasi client.

Wah ada pesan baru. “Please try to add some note(s)”. Sepertinya ini akan berhasil,
silakan coba masukan note baru.

Jika aplikasi Notes Apps masih mengalami kendala same-origin, hal itu mungkin
disebabkan oleh ketentuan keamanan baru yang ditetapkan oleh Google Chrome.
Solusinya, Anda bisa temukkan pada diskusi [Masalah CORS] Aplikasi Client tidak
berubah.

Voila! Akhirnya catatan yang kita masukan tampak yah. Coba masuk ke halaman detail
dengan memilih catatan tersebut.

Yah, eror lagi. Tentu, karena kita belum membuat route untuk mendapatkan catatan
secara spesifik. Ayo kita selesaikan juga.

Kembali ke berkas routes.js, kemudian tambahkan route dengan path ‘/notes/{id}’ dan
method ‘GET’. Untuk handler isi dengan fungsi kosong dulu.

const routes = [
{
method: 'POST',
path: '/notes',
handler: addNoteHandler,
},
{
method: 'GET',
path: '/notes',
handler: getAllNotesHandler,
},
{
method: 'GET',
path: '/notes/{id}',
handler: () => {},
},
];

Lanjut kita buat fungsi handler-nya. Buka berkas handler.js, lalu buat fungsi
dengan nama getNoteByIdHandler.

const getNoteByIdHandler = (request, h) => {

};

Di dalam fungsi ini kita harus mengembalikan objek catatan secara spesifik
berdasarkan id yang digunakan oleh path parameter.
Pertama, kita dapatkan dulu nilai id dari request.params.

const getNoteByIdHandler = (request, h) => {


const { id } = request.params;
};

Setelah mendapatkan nilai id, dapatkan objek note dengan id tersebut dari objek
array notes. Manfaatkan method array filter() untuk mendapatkan objeknya.

const getNoteByIdHandler = (request, h) => {


const { id } = request.params;

const note = notes.filter((n) => n.id === id)[0];


};

Kita kembalikan fungsi handler dengan data beserta objek note di dalamnya. Namun
sebelum itu, pastikan dulu objek note tidak bernilai undefined. Bila undefined,
kembalikan dengan respons gagal.

const getNoteByIdHandler = (request, h) => {


const { id } = request.params;

const note = notes.filter((n) => n.id === id)[0];

if (note !== undefined) {


return {
status: 'success',
data: {
note,
},
};
}
const response = h.response({
status: 'fail',
message: 'Catatan tidak ditemukan',
});
response.code(404);
return response;
};

Fungsi handler selesai! Jangan lupa ekspor fungsinya.

const getNoteByIdHandler = (request, h) => {


const { id } = request.params;

const note = notes.filter((n) => n.id === id)[0];

if (note !== undefined) {


return {
status: 'success',
data: {
note,
},
};
}

const response = h.response({


status: 'fail',
message: 'Catatan tidak ditemukan',
});
response.code(404);
return response;
};

module.exports = { addNoteHandler, getAllNotesHandler, getNoteByIdHandler };

Lanjut gunakan fungsi tersebut pada konfigurasi route di berkas routes.js.

{
method: 'GET',
path: '/notes/{id}',
handler: getNoteByIdHandler,
},

Jangan lupa untuk impor juga yah.

const { addNoteHandler, getAllNotesHandler, getNoteByIdHandler } =


require('./handler');

Simpan seluruh perubahan yang ada dan coba kembali aplikasi client-nya.

Dalam mencobanya, mungkin Anda perlu menambahkan kembali notes karena kita hanya
menyimpannya di array. Di mana data tersebut akan hilang setiap kali server
dijalankan ulang oleh nodemon.

Well done! Sekarang aplikasi sudah bisa menampilkan detail catatan. Di mana di
halaman ini ada tombol “Edit Note”. Bila menekan tombol tersebut, kita akan
diarahkan ke halaman edit note. Tapi halaman tersebut masih belum berfungsi. Nah,
pada materi selanjutnya kita akan buat halaman tersebut berfungsi yah.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Mengubah Catatan
Dua kriteria sudah terpenuhi, kini sebagian dari fitur aplikasi sudah dapat
digunakan. Hanya tinggal sedikit lagi perjalanan kita untuk melengkapi
fungsionalitasnya. Sudah siap menyelesaikan kriteria ketiga? Ayo kita mulai.

Kriteria ketiga adalah web server harus bisa mengubah catatan yang disimpan, baik
perubahan pada title, tags, atau body. Ketika melakukan perubahan, client akan
mengirimkan permintaan ke route ‘/notes/{id}’ dengan method ‘PUT’ dan membawa objek
catatan terbaru pada body request. Yuk langsung saja kita eksekusi.

Seperti biasa, kita awali dengan membuat konfigurasi route-nya dulu. Silakan buka
kembali berkas routes.js, lalu buat route dengan path ‘/notes/{id}’, method ‘PUT’,
dan handler dengan nilai fungsi kosong.

{
method: 'PUT',
path: '/notes/{id}',
handler: () => {},
},

Yuk kita buat fungsi handler-nya pada berkas handler.js. Kita beri nama fungsi
tersebut dengan editNoteByIdHandler ya.
const editNoteByIdHandler = (request, h) => {

};

Catatan yang diubah akan diterapkan sesuai dengan id yang digunakan pada route
parameter. Jadi, kita perlu mendapatkan nilai id-nya terlebih dahulu.

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;
};

Setelah itu, kita dapatkan data notes terbaru yang dikirimkan oleh client melalui
body request.

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;

const { title, tags, body } = request.payload;


};

Selain itu, tentu kita perlu perbarui juga nilai dari properti updatedAt. Jadi,
dapatkan nilai terbaru dengan menggunakan new Date().toISOString().

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;

const { title, tags, body } = request.payload;


const updatedAt = new Date().toISOString();
};

Great! Data terbaru sudah siap, saatnya mengubah catatan lama dengan data terbaru.
Kita akan mengubahnya dengan memanfaatkan indexing array, silakan gunakan teknik
lain bila menurut Anda lebih baik yah.

Pertama, dapatkan dulu index array pada objek catatan sesuai id yang ditentukan.
Untuk melakukannya, gunakanlah method array findIndex().

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;

const { title, tags, body } = request.payload;


const updatedAt = new Date().toISOString();

const index = notes.findIndex((note) => note.id === id);


};

Bila note dengan id yang dicari ditemukan, maka index akan bernilai array index
dari objek catatan yang dicari. Namun bila tidak ditemukan, maka index bernilai -1.
Jadi, kita bisa menentukan gagal atau tidaknya permintaan dari nilai index
menggunakan if else.

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;

const { title, tags, body } = request.payload;


const updatedAt = new Date().toISOString();

const index = notes.findIndex((note) => note.id === id);


if (index !== -1) {
notes[index] = {
...notes[index],
title,
tags,
body,
updatedAt,
};
const response = h.response({
status: 'success',
message: 'Catatan berhasil diperbarui',
});
response.code(200);
return response;
}
const response = h.response({
status: 'fail',
message: 'Gagal memperbarui catatan. Id tidak ditemukan',
});
response.code(404);
return response;
};

Catatan: Spread operator pada kode di atas digunakan untuk mempertahankan nilai
notes[index] yang tidak perlu diubah. Jika Anda butuh mengingat kembali bagaimana
spread operator bekerja, silakan simak pada dokumentasi yang dijelaskan MDN: Spread
Syntax.

Fungsi handler selesai! Jangan lupa export fungsinya.

const editNoteByIdHandler = (request, h) => {


const { id } = request.params;

const { title, tags, body } = request.payload;


const updatedAt = new Date().toISOString();

const index = notes.findIndex((note) => note.id === id);

if (index !== -1) {


notes[index] = {
...notes[index],
title,
tags,
body,
updatedAt,
};

const response = h.response({


status: 'success',
message: 'Catatan berhasil diperbarui',
});
response.code(200);
return response;
}

const response = h.response({


status: 'fail',
message: 'Gagal memperbarui catatan. Id tidak ditemukan',
});
response.code(404);
return response;
};

module.exports = {
addNoteHandler,
getAllNotesHandler,
getNoteByIdHandler,
editNoteByIdHandler,
};

Sekarang kita gunakan fungsinya pada route yah.

{
method: 'PUT',
path: '/notes/{id}',
handler: editNoteByIdHandler,
},

Jangan lupa impor fungsinya.

const {
addNoteHandler,
getAllNotesHandler,
getNoteByIdHandler,
editNoteByIdHandler,
} = require('./handler');

Simpan seluruh perubahan, kemudian silakan coba kembali aplikasi catatannya.


Seharusnya fungsi edit sudah bisa digunakan yah.

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Menghapus Catatan
Tinggal selangkah lagi untuk memenuhi seluruh kriteria yang ada. Saatnya kita
menyelesaikan kriteria terakhir, yakni menghapus catatan. Yuk langsung saja.

Buka kembali berkas routes.js. Tambahkan konfigurasi route dengan nilai path
‘/notes/{id}’, method ‘DELETE’, dan handler dengan fungsi kosong seperti ini:

{
method: 'DELETE',
path: '/notes/{id}',
handler: () => {},
},

Selanjutnya, buat fungsi handler baru dengan nama deleteNoteByIdHandler pada


handler.js.

const deleteNoteByIdHandler = (request, h) => {

};

Setelah itu, saatnya kita menuliskan logikanya. Sama seperti mengubah catatan. Kita
akan memanfaatkan index untuk menghapus catatan.

Pertama, kita dapatkan dulu nilai id yang dikirim melalui path parameters.
const deleteNoteByIdHandler = (request, h) => {
const { id } = request.params;
};

Selanjutnya, dapatkan index dari objek catatan sesuai dengan id yang didapat.

const deleteNoteByIdHandler = (request, h) => {


const { id } = request.params;

const index = notes.findIndex((note) => note.id === id);


};

Lakukan pengecekan terhadap nilai index, pastikan nilainya tidak -1 bila hendak
menghapus catatan. Nah, untuk menghapus data pada array berdasarkan index, gunakan
method array splice().

const deleteNoteByIdHandler = (request, h) => {


const { id } = request.params;

const index = notes.findIndex((note) => note.id === id);

if (index !== -1) {


notes.splice(index, 1);
const response = h.response({
status: 'success',
message: 'Catatan berhasil dihapus',
});
response.code(200);
return response;
}
};

Bila index bernilai -1, maka kembalikan handler dengan respons gagal.

const deleteNoteByIdHandler = (request, h) => {


const { id } = request.params;

const index = notes.findIndex((note) => note.id === id);

if (index !== -1) {


notes.splice(index, 1);
const response = h.response({
status: 'success',
message: 'Catatan berhasil dihapus',
});
response.code(200);
return response;
}

const response = h.response({


status: 'fail',
message: 'Catatan gagal dihapus. Id tidak ditemukan',
});
response.code(404);
return response;
};

Jangan lupa untuk ekspor fungsi handler yah.


const deleteNoteByIdHandler = (request, h) => {
const { id } = request.params;

const index = notes.findIndex((note) => note.id === id);

if (index !== -1) {


notes.splice(index, 1);
const response = h.response({
status: 'success',
message: 'Catatan berhasil dihapus',
});
response.code(200);
return response;
}

const response = h.response({


status: 'fail',
message: 'Catatan gagal dihapus. Id tidak ditemukan',
});
response.code(404);
return response;
};

module.exports = {
addNoteHandler,
getAllNotesHandler,
getNoteByIdHandler,
editNoteByIdHandler,
deleteNoteByIdHandler,
};

Saatnya kita gunakan fungsi handler pada konfigurasi route. Buka berkas routes.js,
lalu tambahkan fungsi handler-nya.

{
method: 'DELETE',
path: '/notes/{id}',
handler: deleteNoteByIdHandler,
},

Jangan lupa untuk mengimpor fungsinya.

const {
addNoteHandler,
getAllNotesHandler,
getNoteByIdHandler,
editNoteByIdHandler,
deleteNoteByIdHandler,
} = require('./handler');

Simpan seluruh perubahan. Setelah itu, silakan coba lagi aplikasi client-nya. Jika
semua berhasil diterapkan, seharusnya fitur hapus catatan sudah berfungsi dengan
baik yah!

Well done! Sebuah kemajuan yang luar biasa! Siap melangkah ke tantangan berikutnya?
Yuk kita lanjutkan perjalanannya!

-----------------------------------------------------------------------------------
-----------------------------------------------------------------------------------
----------------

Anda mungkin juga menyukai