Memulai Dengan Node: Pengantar API, HTTP, dan ES6+ JavaScript

Diterbitkan: 2022-03-10
Ringkasan cepat Pengantar proses pengembangan aplikasi web backend — membahas fitur JavaScript ES6+, HyperText Transfer Protocol, bekerja dengan API dan JSON, dan menggunakan Node.js untuk membangun backend yang cepat dan skalabel.

Anda mungkin pernah mendengar tentang Node.js sebagai "runtime JavaScript asinkron yang dibangun di mesin JavaScript V8 Chrome", dan "menggunakan model I/O non-pemblokiran yang digerakkan oleh peristiwa yang membuatnya ringan dan efisien". Tetapi bagi sebagian orang, itu bukan penjelasan terbaik.

Apa itu Node? Apa sebenarnya artinya Node menjadi “asynchronous”, dan apa bedanya dengan “synchronous”? Apa arti "event-driven" dan "non-blocking", dan bagaimana Node cocok dengan gambaran yang lebih besar dari aplikasi, jaringan Internet, dan server?

Kami akan mencoba menjawab semua pertanyaan ini dan lebih banyak lagi sepanjang seri ini saat kami melihat secara mendalam cara kerja Node, mempelajari tentang HyperText Transfer Protocol, API, dan JSON, dan membangun API Rak Buku kami sendiri dengan memanfaatkan MongoDB, Express, Lodash, Mocha, dan Handlebars.

Apa itu Node.js

Node hanyalah sebuah lingkungan, atau runtime, di mana untuk menjalankan JavaScript normal (dengan perbedaan kecil) di luar browser. Kita dapat menggunakannya untuk membangun aplikasi desktop (dengan kerangka kerja seperti Electron), menulis server web atau aplikasi, dan banyak lagi.

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Pemblokiran/Non-Pemblokiran Dan Sinkron/Asinkron

Misalkan kita membuat panggilan database untuk mengambil properti tentang pengguna. Panggilan itu akan memakan waktu, dan jika permintaannya "memblokir", maka itu berarti itu akan memblokir eksekusi program kita sampai panggilan selesai. Dalam hal ini, kami membuat permintaan "sinkron" karena akhirnya memblokir utas.

Jadi, operasi sinkron memblokir proses atau utas hingga operasi itu selesai, meninggalkan utas dalam "status tunggu". Operasi asinkron , di sisi lain, adalah non-blocking . Ini memungkinkan eksekusi utas untuk melanjutkan terlepas dari waktu yang diperlukan untuk menyelesaikan operasi atau hasil yang diselesaikannya, dan tidak ada bagian dari utas yang jatuh ke status tunggu di titik mana pun.

Mari kita lihat contoh lain dari panggilan sinkron yang memblokir utas. Misalkan kita sedang membangun sebuah aplikasi yang membandingkan hasil dari dua API Cuaca untuk menemukan persen perbedaan suhunya. Dengan cara memblokir, kami melakukan panggilan ke Weather API One dan menunggu hasilnya. Setelah kami mendapatkan hasilnya, kami memanggil Weather API Two dan menunggu hasilnya. Jangan khawatir pada titik ini jika Anda tidak terbiasa dengan API. Kami akan membahasnya di bagian yang akan datang. Untuk saat ini, anggap saja API sebagai media di mana dua komputer dapat berkomunikasi satu sama lain.

Grafik yang menggambarkan fakta bahwa Operasi Sinkron membutuhkan waktu lama untuk diselesaikan
Perkembangan waktu operasi pemblokiran sinkron (Pratinjau besar)

Izinkan saya untuk mencatat, penting untuk diketahui bahwa tidak semua panggilan sinkron harus diblokir. Jika operasi sinkron dapat diselesaikan tanpa memblokir utas atau menyebabkan status menunggu, itu bukan pemblokiran. Sebagian besar waktu, panggilan sinkron akan diblokir, dan waktu yang dibutuhkan untuk menyelesaikannya akan bergantung pada berbagai faktor, seperti kecepatan server API, kecepatan unduhan koneksi internet pengguna akhir, dll.

Dalam kasus gambar di atas, kami harus menunggu cukup lama untuk mengambil hasil pertama dari API One. Setelah itu, kami harus menunggu sama lama untuk mendapatkan respon dari API Two. Sambil menunggu kedua tanggapan, pengguna akan melihat aplikasi kami hang — UI benar-benar terkunci — dan itu akan berdampak buruk bagi Pengalaman Pengguna.

Dalam kasus panggilan non-pemblokiran, kami akan memiliki sesuatu seperti ini:

Grafik yang menggambarkan fakta bahwa Operasi Non-Blokir Asinkron hampir 50 persen lebih cepat
Perkembangan waktu operasi non-pemblokiran asinkron (Pratinjau besar)

Anda dapat dengan jelas melihat seberapa cepat kami menyelesaikan eksekusi. Daripada menunggu di API One dan kemudian menunggu di API Two, kita bisa menunggu keduanya selesai pada saat yang sama dan mencapai hasil kita hampir 50% lebih cepat. Perhatikan, setelah kami memanggil API One dan mulai menunggu responsnya, kami juga memanggil API Two dan mulai menunggu responsnya pada saat yang sama dengan One.

Pada titik ini, sebelum beralih ke contoh yang lebih konkret dan nyata, penting untuk disebutkan bahwa, untuk memudahkan, istilah "Sinkron" umumnya disingkat menjadi "Sinkron", dan istilah "Asinkron" umumnya disingkat menjadi "Asinkron". Anda akan melihat notasi ini digunakan dalam nama metode/fungsi.

Fungsi Panggilan Balik

Anda mungkin bertanya-tanya, “jika kami dapat menangani panggilan secara asinkron, bagaimana kami tahu kapan panggilan itu selesai dan kami mendapat tanggapan?” Umumnya, kami meneruskan sebagai argumen ke metode async kami fungsi panggilan balik, dan metode itu akan "memanggil kembali" fungsi itu di lain waktu dengan respons. Saya menggunakan fungsi ES5 di sini, tetapi kami akan memperbarui ke standar ES6 nanti.

 function asyncAddFunction(a, b, callback) { callback(a + b); //This callback is the one passed in to the function call below. } asyncAddFunction(2, 4, function(sum) { //Here we have the sum, 2 + 4 = 6. });

Fungsi seperti itu disebut "Fungsi Tingkat Tinggi" karena ia mengambil fungsi (panggilan balik kami) sebagai argumen. Atau, fungsi panggilan balik mungkin mengambil objek kesalahan dan objek respons sebagai argumen, dan menampilkannya saat fungsi asinkron selesai. Kita akan melihat ini nanti dengan Express. Saat kami memanggil asyncAddFunction(...) , Anda akan melihat kami menyediakan fungsi panggilan balik untuk parameter panggilan balik dari definisi metode. Fungsi ini adalah fungsi anonim (tidak memiliki nama) dan ditulis menggunakan Sintaks Ekspresi . Definisi metode, di sisi lain, adalah pernyataan fungsi. Ini bukan anonim karena sebenarnya memiliki nama (yaitu "asyncAddFunction").

Beberapa mungkin mencatat kebingungan karena, dalam definisi metode, kami menyediakan nama, yang menjadi "panggilan balik". Namun, fungsi anonim yang diteruskan sebagai parameter ketiga ke asyncAddFunction(...) tidak mengetahui tentang nama tersebut, sehingga tetap anonim. Kami juga tidak dapat menjalankan fungsi itu di lain waktu berdasarkan nama, kami harus melalui fungsi panggilan async lagi untuk mengaktifkannya.

Sebagai contoh panggilan sinkron, kita dapat menggunakan metode readFileSync readFileSync(...) Node.js. Sekali lagi, kita akan pindah ke ES6+ nanti.

 var fs = require('fs'); var data = fs.readFileSync('/example.txt'); // The thread will be blocked here until complete.

Jika kami melakukan ini secara tidak sinkron, kami akan meneruskan fungsi panggilan balik yang akan diaktifkan saat operasi asinkron selesai.

 var fs = require('fs'); var data = fs.readFile('/example.txt', function(err, data) { //Move on, this will fire when ready. if(err) return console.log('Error: ', err); console.log('Data: ', data); // Assume var data is defined above. }); // Keep executing below, don't wait on the data.

Jika Anda belum pernah melihat return yang digunakan dengan cara itu sebelumnya, kami hanya mengatakan untuk menghentikan eksekusi fungsi sehingga kami tidak mencetak objek data jika objek kesalahan didefinisikan. Kita juga bisa saja membungkus pernyataan log dalam klausa else .

Seperti asyncAddFunction(...) , kode di belakang fungsi fs.readFile(...) akan menjadi sesuatu seperti:

 function readFile(path, callback) { // Behind the scenes code to read a file stream. // The data variable is defined up here. callback(undefined, data); //Or, callback(err, undefined); }

Izinkan kami untuk melihat satu implementasi terakhir dari panggilan fungsi async. Ini akan membantu memperkuat gagasan tentang fungsi panggilan balik yang diaktifkan di lain waktu, dan ini akan membantu kita memahami eksekusi program Node.js yang khas.

 setTimeout(function() { // ... }, 1000);

Metode setTimeout(...) mengambil fungsi panggilan balik untuk parameter pertama yang akan diaktifkan setelah jumlah milidetik yang ditentukan saat argumen kedua terjadi.

Mari kita lihat contoh yang lebih kompleks:

 console.log('Initiated program.'); setTimeout(function() { console.log('3000 ms (3 sec) have passed.'); }, 3000); setTimeout(function() { console.log('0 ms (0 sec) have passed.'); }, 0); setTimeout(function() { console.log('1000 ms (1 sec) has passed.'); }, 1000); console.log('Terminated program');

Output yang kami terima adalah:

 Initiated program. Terminated program. 0 ms (0 sec) have passed. 1000 ms (1 sec) has passed. 3000 ms (3 sec) have passed.

Anda dapat melihat bahwa pernyataan log pertama berjalan seperti yang diharapkan. Seketika, pernyataan log terakhir dicetak ke layar, untuk itu terjadi sebelum 0 detik telah terlampaui setelah setTimeout(...) kedua. Segera setelah itu, metode setTimeout(...) kedua, ketiga, dan pertama dijalankan.

Jika Node.js tidak non-blocking, kita akan melihat pernyataan log pertama, tunggu 3 detik untuk melihat berikutnya, langsung lihat yang ketiga (0-detik setTimeout(...) , dan kemudian harus menunggu satu lagi kedua untuk melihat dua pernyataan log terakhir. Sifat Node yang non-blocking membuat semua timer mulai menghitung mundur dari saat program dijalankan, bukan urutan saat mereka diketik. Anda mungkin ingin melihat ke dalam Node API, Callstack, dan Event Loop untuk informasi lebih lanjut tentang bagaimana Node bekerja di bawah tenda.

Penting untuk dicatat bahwa hanya karena Anda melihat fungsi panggilan balik tidak berarti ada panggilan asinkron dalam kode. Kami menyebut metode asyncAddFunction(…) di atas “async” karena kami mengasumsikan operasi membutuhkan waktu untuk diselesaikan — seperti melakukan panggilan ke server. Pada kenyataannya, proses penambahan dua angka tidak asinkron, dan itu sebenarnya akan menjadi contoh penggunaan fungsi panggilan balik dengan cara yang tidak benar-benar memblokir utas.

Janji Atas Panggilan Balik

Panggilan balik dapat dengan cepat menjadi berantakan di JavaScript, terutama beberapa panggilan balik bersarang. Kita terbiasa dengan melewatkan callback sebagai argumen ke suatu fungsi, tetapi Promises memungkinkan kita untuk memasang, atau melampirkan, callback ke objek yang dikembalikan dari suatu fungsi. Ini akan memungkinkan kami menangani beberapa panggilan asinkron dengan cara yang lebih elegan.

Sebagai contoh, misalkan kita membuat panggilan API, dan fungsi kita, tidak begitu unik bernama ' makeAPICall(...) ', mengambil URL dan panggilan balik.

Fungsi kita, makeAPICall(...) , akan didefinisikan sebagai

 function makeAPICall(path, callback) { // Attempt to make API call to path argument. // ... callback(undefined, res); // Or, callback(err, undefined); depending upon the API's response. }

dan kami akan menyebutnya dengan:

 makeAPICall('/example', function(err1, res1) { if(err1) return console.log('Error: ', err1); // ... });

Jika kami ingin membuat panggilan API lain menggunakan respons dari yang pertama, kami harus menyarangkan kedua panggilan balik. Misalkan saya perlu menyuntikkan properti userName dari objek res1 ke jalur panggilan API kedua. Kami akan memiliki:

 makeAPICall('/example', function(err1, res1) { if(err1) return console.log('Error: ', err1); makeAPICall('/newExample/' + res1.userName, function(err2, res2) { if(err2) return console.log('Error: ', err2); console.log(res2); }); });

Catatan : Metode ES6+ untuk menginjeksi properti res1.userName daripada penggabungan string adalah dengan menggunakan "Template Strings". Dengan begitu, daripada merangkum string kita dalam tanda kutip ( ' , atau " ), kita akan menggunakan backticks ( ` ). yang terletak di bawah tombol Escape pada keyboard Anda. Kemudian, kita akan menggunakan notasi ${} untuk menyematkan ekspresi JS apa pun di dalamnya tanda kurung Pada akhirnya, jalur kita sebelumnya adalah: /newExample/${res.UserName} , dibungkus dengan backticks.

Jelas terlihat bahwa metode panggilan balik bersarang ini dapat dengan cepat menjadi sangat tidak elegan, yang disebut "Piramida Kiamat JavaScript". Langsung masuk, jika kami menggunakan janji daripada panggilan balik, kami dapat memfaktorkan ulang kode kami dari contoh pertama seperti:

 makeAPICall('/example').then(function(res) { // Success callback. // ... }, function(err) { // Failure callback. console.log('Error:', err); });

Argumen pertama untuk fungsi then() adalah callback sukses kita, dan argumen kedua adalah callback kegagalan kita. Atau, kita bisa kehilangan argumen kedua menjadi .then() , dan memanggil .catch() sebagai gantinya. Argumen ke .then() adalah opsional, dan memanggil .catch() akan setara dengan .then(successCallback, null) .

Menggunakan .catch() , kita memiliki:

 makeAPICall('/example').then(function(res) { // Success callback. // ... }).catch(function(err) { // Failure Callback console.log('Error: ', err); });

Kami juga dapat merestrukturisasi ini agar mudah dibaca:

 makeAPICall('/example') .then(function(res) { // ... }) .catch(function(err) { console.log('Error: ', err); });

Penting untuk dicatat bahwa kita tidak bisa hanya memasang panggilan .then() ke fungsi apa pun dan mengharapkannya berfungsi. Fungsi yang kita panggil harus benar-benar mengembalikan sebuah janji, sebuah janji yang akan mengaktifkan .then() saat operasi asinkron itu selesai. Dalam hal ini, makeAPICall(...) akan melakukannya, menembakkan blok then() atau blok catch() saat selesai.

Untuk membuat makeAPICall(...) mengembalikan Janji, kami menetapkan fungsi ke variabel, di mana fungsi itu adalah konstruktor Janji. Janji dapat dipenuhi atau ditolak , di mana terpenuhi berarti tindakan yang berkaitan dengan janji berhasil diselesaikan, dan ditolak berarti sebaliknya. Setelah janji dipenuhi atau ditolak, kami mengatakan itu telah diselesaikan , dan sambil menunggu untuk diselesaikan, mungkin selama panggilan asinkron, kami mengatakan bahwa janji itu tertunda .

Konstruktor Promise menggunakan satu fungsi callback sebagai argumen, yang menerima dua parameter — resolve dan reject , yang akan kita panggil di lain waktu untuk mengaktifkan callback yang berhasil di .then() , atau kegagalan .then() panggilan balik, atau .catch() , jika disediakan.

Berikut adalah contoh tampilannya:

 var examplePromise = new Promise(function(resolve, reject) { // Do whatever we are going to do and then make the appropiate call below: resolve('Happy!'); // — Everything worked. reject('Sad!'); // — We noticed that something went wrong. }):

Kemudian, kita dapat menggunakan:

 examplePromise.then(/* Both callback functions in here */); // Or, the success callback in .then() and the failure callback in .catch().

Namun, perhatikan bahwa examplePromise tidak dapat menerima argumen apa pun. Hal semacam itu mengalahkan tujuannya, jadi kita bisa mengembalikan janji sebagai gantinya.

 function makeAPICall(path) { return new Promise(function(resolve, reject) { // Make our async API call here. if (/* All is good */) return resolve(res); //res is the response, would be defined above. else return reject(err); //err is error, would be defined above. }); }

Janji benar-benar bersinar untuk meningkatkan struktur, dan selanjutnya, keanggunan, kode kami dengan konsep "Rantai Janji". Ini akan memungkinkan kita untuk mengembalikan Promise baru di dalam klausa .then() , sehingga kita dapat melampirkan .then() kedua setelahnya, yang akan mengaktifkan callback yang sesuai dari promise kedua.

Memfaktorkan ulang panggilan URL multi API kami di atas dengan Janji, kami mendapatkan:

 makeAPICall('/example').then(function(res) { // First response callback. Fires on success to '/example' call. return makeAPICall(`/newExample/${res.UserName}`); // Returning new call allows for Promise Chaining. }, function(err) { // First failure callback. Fires if there is a failure calling with '/example'. console.log('Error:', err); }).then(function(res) { // Second response callback. Fires on success to returned '/newExample/...' call. console.log(res); }, function(err) { // Second failure callback. Fire if there is a failure calling with '/newExample/...' console.log('Error:', err); });

Perhatikan bahwa pertama kita memanggil makeAPICall('/example') . Itu mengembalikan janji, jadi kami melampirkan .then() . Di dalam then() , kita mengembalikan panggilan baru ke makeAPICall(...) , yang, dengan sendirinya, seperti yang terlihat sebelumnya, mengembalikan sebuah promise, yang memungkinkan kita menyambungkan .then( .then() baru setelah yang pertama.

Seperti di atas, kita dapat merestrukturisasi ini agar mudah dibaca, dan menghapus callback kegagalan untuk klausa catch() semua generik. Kemudian, kita bisa mengikuti Prinsip KERING (Don't Repeat Yourself), dan hanya perlu menerapkan penanganan kesalahan satu kali.

 makeAPICall('/example') .then(function(res) { // Like earlier, fires with success and response from '/example'. return makeAPICall(`/newExample/${res.UserName}`); // Returning here lets us chain on a new .then(). }) .then(function(res) { // Like earlier, fires with success and response from '/newExample'. console.log(res); }) .catch(function(err) { // Generic catch all method. Fires if there is an err with either earlier call. console.log('Error: ', err); });

Perhatikan bahwa keberhasilan dan kegagalan callback di .then() hanya diaktifkan untuk status Promise individual yang terkait dengan .then() . Namun, blok catch akan menangkap kesalahan apa pun yang muncul di salah satu .then() s.

ES6 Const vs. Let

Sepanjang semua contoh kami, kami telah menggunakan fungsi ES5 dan kata kunci var lama. Sementara jutaan baris kode masih berjalan hari ini menggunakan metode ES5 tersebut, akan berguna untuk memperbarui ke standar ES6+ saat ini, dan kami akan memfaktorkan ulang beberapa kode kami di atas. Mari kita mulai dengan const dan let .

Anda mungkin terbiasa mendeklarasikan variabel dengan kata kunci var :

 var pi = 3.14;

Dengan standar ES6+, kami juga bisa membuatnya

 let pi = 3.14;

atau

 const pi = 3.14;

di mana const berarti "konstan" — nilai yang tidak dapat ditetapkan kembali nanti. (Kecuali untuk properti objek — kami akan segera membahasnya. Selain itu, variabel yang dideklarasikan const tidak dapat diubah, hanya referensi ke variabelnya.)

Dalam JavaScript lama, blokir cakupan, seperti yang ada di if , while , {} . for , dll. tidak memengaruhi var dengan cara apa pun, dan ini sangat berbeda dengan bahasa yang diketik secara statis seperti Java atau C++. Artinya, ruang lingkup var adalah seluruh fungsi terlampir — dan itu bisa bersifat global (jika ditempatkan di luar fungsi), atau lokal (jika ditempatkan di dalam suatu fungsi). Untuk mendemonstrasikannya, lihat contoh berikut:

 function myFunction() { var num = 5; console.log(num); // 5 console.log('--'); for(var i = 0; i < 10; i++) { var num = i; console.log(num); //num becomes 0 — 9 } console.log('--'); console.log(num); // 9 console.log(i); // 10 } myFunction();

Keluaran:

 5 --- 0 1 2 3 ... 7 8 9 --- 9 10

Hal penting untuk diperhatikan di sini adalah bahwa mendefinisikan var num baru di dalam for scope secara langsung memengaruhi var num di luar dan di atas for . Ini karena ruang lingkup var selalu dari fungsi terlampir, dan bukan blok.

Sekali lagi, secara default, var i inside for() default ke lingkup myFunction , sehingga kita dapat mengakses i di luar loop dan mendapatkan 10.

Dalam hal memberikan nilai ke variabel, let sama dengan var , hanya saja let memiliki scoping blok, sehingga anomali yang terjadi dengan var di atas tidak akan terjadi.

 function myFunction() { let num = 5; console.log(num); // 5 for(let i = 0; i < 10; i++) { let num = i; console.log('--'); console.log(num); // num becomes 0 — 9 } console.log('--'); console.log(num); // 5 console.log(i); // undefined, ReferenceError }

Melihat kata kunci const , Anda dapat melihat bahwa kami mendapatkan kesalahan jika kami mencoba menetapkan ulang ke sana:

 const c = 299792458; // Fact: The constant "c" is the speed of light in a vacuum in meters per second. c = 10; // TypeError: Assignment to constant variable.

Hal-hal menjadi menarik ketika kita menetapkan variabel const ke objek:

 const myObject = { name: 'Jane Doe' }; // This is illegal: TypeError: Assignment to constant variable. myObject = { name: 'John Doe' }; // This is legal. console.log(myObject.name) -> John Doe myObject.name = 'John Doe';

Seperti yang Anda lihat, hanya referensi dalam memori ke objek yang ditetapkan ke objek const yang tidak dapat diubah, bukan nilainya sendiri.

Fungsi Panah ES6

Anda mungkin terbiasa membuat fungsi seperti ini:

 function printHelloWorld() { console.log('Hello, World!'); }

Dengan fungsi panah, itu akan menjadi:

 const printHelloWorld = () => { console.log('Hello, World!'); };

Misalkan kita memiliki fungsi sederhana yang mengembalikan kuadrat suatu bilangan:

 const squareNumber = (x) => { return x * x; } squareNumber(5); // We can call an arrow function like an ES5 functions. Returns 25.

Anda dapat melihat bahwa, seperti halnya fungsi ES5, kita dapat menerima argumen dengan tanda kurung, kita dapat menggunakan pernyataan pengembalian normal, dan kita dapat memanggil fungsi tersebut sama seperti fungsi lainnya.

Penting untuk dicatat bahwa, meskipun tanda kurung diperlukan jika fungsi kita tidak membutuhkan argumen (seperti dengan printHelloWorld() di atas), kita dapat menghapus tanda kurung jika hanya membutuhkan satu, jadi definisi metode squareNumber() kita sebelumnya dapat ditulis ulang sebagai:

 const squareNumber = x => { // Notice we have dropped the parentheses for we only take in one argument. return x * x; }

Apakah Anda memilih untuk merangkum satu argumen dalam tanda kurung atau tidak adalah masalah selera pribadi, dan Anda mungkin akan melihat pengembang menggunakan kedua metode tersebut.

Terakhir, jika kita hanya ingin mengembalikan satu ekspresi secara implisit, seperti squareNumber(...) di atas, kita dapat menempatkan pernyataan return sejalan dengan tanda tangan metode:

 const squareNumber = x => x * x;

Itu adalah,

 const test = (a, b, c) => expression

sama dengan

 const test = (a, b, c) => { return expression }

Catatan, saat menggunakan singkatan di atas untuk mengembalikan objek secara implisit, hal-hal menjadi tidak jelas. Apa yang menghentikan JavaScript dari mempercayai tanda kurung di mana kita diminta untuk merangkum objek kita bukan badan fungsi kita? Untuk menyiasatinya, kami membungkus tanda kurung objek dalam tanda kurung. Ini secara eksplisit memberi tahu JavaScript bahwa kami memang mengembalikan objek, dan kami tidak hanya mendefinisikan tubuh.

 const test = () => ({ pi: 3.14 }); // Spaces between brackets are a formality to make the code look cleaner.

Untuk membantu memperkuat konsep fungsi ES6, kami akan memfaktorkan ulang beberapa kode kami sebelumnya yang memungkinkan kami untuk membandingkan perbedaan antara kedua notasi.

asyncAddFunction(...) , dari atas, dapat difaktorkan ulang dari:

 function asyncAddFunction(a, b, callback){ callback(a + b); }

ke:

 const aysncAddFunction = (a, b, callback) => { callback(a + b); };

atau bahkan ke:

 const aysncAddFunction = (a, b, callback) => callback(a + b); // This will return callback(a + b).

Saat memanggil fungsi, kita bisa memasukkan fungsi panah untuk panggilan balik:

 asyncAddFunction(10, 12, sum => { // No parentheses because we only take one argument. console.log(sum); }

Jelas terlihat bagaimana metode ini meningkatkan keterbacaan kode. Untuk menunjukkan kepada Anda hanya satu kasus, kami dapat mengambil contoh berbasis ES5 Promise lama kami di atas, dan memfaktorkannya kembali menggunakan fungsi panah.

 makeAPICall('/example') .then(res => makeAPICall(`/newExample/${res.UserName}`)) .then(res => console.log(res)) .catch(err => console.log('Error: ', err));

Sekarang, ada beberapa peringatan dengan fungsi panah. Pertama, mereka tidak mengikat kata kunci this . Misalkan saya memiliki objek berikut:

 const Person = { name: 'John Doe', greeting: () => { console.log(`Hi. My name is ${this.name}.`); } }

Anda mungkin mengharapkan panggilan ke Person.greeting() akan mengembalikan “Hai. Nama saya John Doe.” Sebagai gantinya, kita mendapatkan: “Hai. Nama saya tidak terdefinisi.” Itu karena fungsi panah tidak memiliki this , dan karenanya mencoba menggunakan this di dalam fungsi panah default ke this dari lingkup terlampir, dan lingkup terlampir dari objek Person adalah window , di browser, atau module.exports di simpul.

Untuk membuktikannya, jika kita menggunakan objek yang sama lagi, tetapi tetapkan properti name dari global this menjadi sesuatu seperti 'Jane Doe', maka this.name dalam fungsi panah mengembalikan 'Jane Doe', karena global this berada di dalam lingkup terlampir, atau merupakan induk dari objek Person .

 this.name = 'Jane Doe'; const Person = { name: 'John Doe', greeting: () => { console.log(`Hi. My name is ${this.name}.`); } } Person.greeting(); // Hi. My name is Jane Doe

Ini dikenal sebagai 'Pelingkupan Lexical', dan kita bisa menyiasatinya dengan menggunakan apa yang disebut 'Sintaks Pendek', di mana kita kehilangan titik dua dan panah untuk memfaktorkan ulang objek kita seperti:

 const Person = { name: 'John Doe', greeting() { console.log(`Hi. My name is ${this.name}.`); } } Person.greeting() //Hi. My name is John Doe.

Kelas ES6

Meskipun JavaScript tidak pernah mendukung kelas, Anda selalu dapat menirunya dengan objek seperti di atas. EcmaScript 6 menyediakan dukungan untuk kelas menggunakan class dan kata kunci new :

 class Person { constructor(name) { this.name = name; } greeting() { console.log(`Hi. My name is ${this.name}.`); } } const person = new Person('John'); person.greeting(); // Hi. My name is John.

Fungsi konstruktor dipanggil secara otomatis saat menggunakan kata kunci new , di mana kita dapat meneruskan argumen untuk mengatur objek pada awalnya. Ini harusnya familiar bagi setiap pembaca yang memiliki pengalaman dengan bahasa pemrograman berorientasi objek yang lebih diketik secara statis seperti Java, C++, dan C#.

Tanpa terlalu banyak detail tentang konsep OOP, paradigma lain seperti itu adalah "warisan", yang memungkinkan satu kelas untuk mewarisi dari yang lain. Kelas bernama Car , misalnya, akan sangat umum — berisi metode seperti “stop”, “start”, dll., yang dibutuhkan semua mobil. Sebuah sub-set dari kelas yang disebut SportsCar , kemudian, mungkin mewarisi operasi dasar dari Car dan menimpa apa pun yang dibutuhkan kustom. Kita bisa menunjukkan kelas seperti itu sebagai berikut:

 class Car { constructor(licensePlateNumber) { this.licensePlateNumber = licensePlateNumber; } start() {} stop() {} getLicensePlate() { return this.licensePlateNumber; } // … } class SportsCar extends Car { constructor(engineRevCount, licensePlateNumber) { super(licensePlateNumber); // Pass licensePlateNumber up to the parent class. this.engineRevCount = engineRevCount; } start() { super.start(); } stop() { super.stop(); } getLicensePlate() { return super.getLicensePlate(); } getEngineRevCount() { return this.engineRevCount; } }

Anda dapat dengan jelas melihat bahwa kata kunci super memungkinkan kita untuk mengakses properti dan metode dari kelas induk, atau super.

Acara JavaScript

Peristiwa adalah tindakan yang terjadi di mana Anda memiliki kemampuan untuk merespons. Misalkan Anda sedang membangun formulir login untuk aplikasi Anda. Saat pengguna menekan tombol "kirim", Anda dapat bereaksi terhadap peristiwa itu melalui "penangan peristiwa" dalam kode Anda — biasanya sebuah fungsi. Ketika fungsi ini didefinisikan sebagai event handler, kita katakan bahwa kita "mendaftarkan event handler". Penangan acara untuk klik tombol kirim kemungkinan akan memeriksa pemformatan input yang diberikan oleh pengguna, membersihkannya untuk mencegah serangan seperti SQL Injection atau Cross Site Scripting (perlu diketahui bahwa tidak ada kode di sisi klien yang dapat dipertimbangkan aman Selalu bersihkan data di server — jangan pernah memercayai apa pun dari browser), lalu periksa untuk melihat apakah kombinasi nama pengguna dan kata sandi itu keluar dalam database untuk mengautentikasi pengguna dan memberikan mereka token.

Karena ini adalah artikel tentang Node, kami akan fokus pada Model Event Node.

Kita dapat menggunakan modul events dari Node untuk memancarkan dan bereaksi terhadap event tertentu. Objek apa pun yang memancarkan peristiwa adalah turunan dari kelas EventEmitter .

Kita dapat memancarkan suatu peristiwa dengan memanggil metode emit() dan kita mendengarkan peristiwa itu melalui metode on() , yang keduanya diekspos melalui kelas EventEmitter .

 const EventEmitter = require('events'); const myEmitter = new EventEmitter();

Dengan myEmitter sekarang menjadi instance dari kelas EventEmitter , kita dapat mengakses emit emit() dan on() :

 const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('someEvent', () => { console.log('The "someEvent" event was fired (emitted)'); }); myEmitter.emit('someEvent'); // This will call the callback function above.

Parameter kedua untuk myEmitter.on() adalah fungsi callback yang akan diaktifkan saat event dipancarkan — ini adalah event handler. Parameter pertama adalah nama acara, yang dapat berupa apa saja yang kita suka, meskipun konvensi penamaan camelCase direkomendasikan.

Selain itu, event handler dapat mengambil sejumlah argumen, yang diturunkan saat event dipancarkan:

 const EventEmitter = require('events'); const myEmitter = new EventEmitter(); myEmitter.on('someEvent', (data) => { console.log(`The "someEvent" event was fired (emitted) with data: ${data}`); }); myEmitter.emit('someEvent', 'This is the data payload');

Dengan menggunakan warisan, kita dapat mengekspos metode emit emit() dan on() dari 'EventEmitter' ke kelas mana pun. Ini dilakukan dengan membuat kelas Node.js, dan menggunakan kata kunci yang dicadangkan untuk mewarisi properti yang tersedia EventEmitter extends

 const EventEmitter = require('events'); class MyEmitter extends EventEmitter { // This is my class. I can emit events from a MyEmitter object. }

Misalkan kita sedang membangun program pemberitahuan tabrakan kendaraan yang menerima data dari giroskop, akselerometer, dan pengukur tekanan pada lambung mobil. Saat kendaraan bertabrakan dengan objek, sensor eksternal tersebut akan mendeteksi tabrakan, menjalankan fungsi collide(...) dan meneruskan data sensor teragregasi sebagai Objek JavaScript yang bagus. Fungsi ini akan mengeluarkan peristiwa collision , memberi tahu vendor tentang kerusakan tersebut.

 const EventEmitter = require('events'); class Vehicle extends EventEmitter { collide(collisionStatistics) { this.emit('collision', collisionStatistics) } } const myVehicle = new Vehicle(); myVehicle.on('collision', collisionStatistics => { console.log('WARNING! Vehicle Impact Detected: ', collisionStatistics); notifyVendor(collisionStatistics); }); myVehicle.collide({ ... });

Ini adalah contoh yang berbelit-belit karena kita bisa saja meletakkan kode di dalam event handler di dalam fungsi clash dari kelas, tetapi ini menunjukkan bagaimana Node Event Model tetap berfungsi. Perhatikan bahwa beberapa tutorial akan menunjukkan metode util.inherits() yang mengizinkan objek untuk memancarkan peristiwa. Itu telah ditinggalkan demi Kelas ES6 dan extends .

Manajer Paket Node

Saat memprogram dengan Node dan JavaScript, akan sangat umum mendengar tentang npm . Npm adalah manajer paket yang melakukan hal itu — mengizinkan pengunduhan paket pihak ketiga yang memecahkan masalah umum dalam JavaScript. Solusi lain, seperti Yarn, Npx, Grunt, dan Bower juga ada, tetapi di bagian ini, kami hanya akan fokus pada npm dan bagaimana Anda dapat menginstal dependensi untuk aplikasi Anda melalui Command Line Interface (CLI) sederhana yang menggunakannya.

Mari kita mulai dengan sederhana, hanya dengan npm . Kunjungi beranda NpmJS untuk melihat semua paket yang tersedia dari NPM. Saat Anda memulai proyek baru yang akan bergantung pada Paket NPM, Anda harus menjalankan npm init melalui terminal di direktori root proyek Anda. Anda akan ditanya serangkaian pertanyaan yang akan digunakan untuk membuat file package.json . File ini menyimpan semua dependensi Anda — modul tempat aplikasi Anda bergantung untuk berfungsi, skrip — perintah terminal yang telah ditentukan sebelumnya untuk menjalankan tes, membangun proyek, memulai server pengembangan, dll., dan banyak lagi.

Untuk menginstal sebuah paket, jalankan npm install [package-name] --save . Bendera save akan memastikan paket dan versinya dicatat dalam file package.json . Sejak npm versi 5, dependensi disimpan secara default, jadi --save dapat dihilangkan. Anda juga akan melihat folder node_modules baru, yang berisi kode untuk paket yang baru saja Anda instal. Ini juga dapat disingkat menjadi npm i [package-name] . Sebagai catatan yang berguna, folder node_modules tidak boleh disertakan dalam repositori GitHub karena ukurannya. Setiap kali Anda mengkloning repo dari GitHub (atau sistem manajemen versi lainnya), pastikan untuk menjalankan perintah npm install untuk keluar dan mengambil semua paket yang ditentukan dalam file package.json , membuat direktori node_modules secara otomatis. Anda juga dapat menginstal paket pada versi tertentu: npm i [package-name]@1.10.1 --save , misalnya.

Menghapus sebuah paket mirip dengan menginstal satu: npm remove [package-name] .

Anda juga dapat menginstal paket secara global. Paket ini akan tersedia di semua proyek, bukan hanya proyek yang sedang Anda kerjakan. Anda melakukan ini dengan flag -g setelah npm i [package-name] . Ini biasanya digunakan untuk CLI, seperti Google Firebase dan Heroku. Terlepas dari kemudahan yang diberikan metode ini, umumnya dianggap praktik yang buruk untuk menginstal paket secara global, karena paket tersebut tidak disimpan dalam file package.json , dan jika pengembang lain mencoba menggunakan proyek Anda, mereka tidak akan mencapai semua dependensi yang diperlukan dari npm install .

API & JSON

API adalah paradigma yang sangat umum dalam pemrograman, dan bahkan jika Anda baru memulai karir sebagai pengembang, API dan penggunaannya, terutama dalam pengembangan web dan seluler, kemungkinan akan muncul lebih sering daripada tidak.

API adalah Antarmuka Pemrograman Aplikasi , dan pada dasarnya adalah metode di mana dua sistem yang dipisahkan dapat berkomunikasi satu sama lain. Dalam istilah yang lebih teknis, API mengizinkan sistem atau program komputer (biasanya server) untuk menerima permintaan dan mengirim tanggapan yang sesuai (ke klien, juga dikenal sebagai tuan rumah).

Misalkan Anda sedang membangun aplikasi cuaca. Anda memerlukan cara untuk membuat geocode alamat pengguna ke dalam garis lintang dan garis bujur, dan kemudian cara untuk mendapatkan cuaca saat ini atau perkiraan cuaca di lokasi tertentu.

As a developer, you want to focus on building your app and monetizing it, not putting the infrastructure in place to geocode addresses or placing weather stations in every city.

Luckily for you, companies like Google and OpenWeatherMap have already put that infrastructure in place, you just need a way to talk to it — that is where the API comes in. While, as of now, we have developed a very abstract and ambiguous definition of the API, bear with me. We'll be getting to tangible examples soon.

Now, it costs money for companies to develop, maintain, and secure that aforementioned infrastructure, and so it is common for corporations to sell you access to their API. This is done with that is known as an API key, a unique alphanumeric identifier associating you, the developer, with the API. Every time you ask the API to send you data, you pass along your API key. The server can then authenticate you and keep track of how many API calls you are making, and you will be charged appropriately. The API key also permits Rate-Limiting or API Call Throttling (a method of throttling the number of API calls in a certain timeframe as to not overwhelm the server, preventing DOS attacks — Denial of Service). Most companies, however, will provide a free quota, giving you, as an example, 25,000 free API calls a day before charging you.

Up to this point, we have established that an API is a method by which two computer programs can communicate with each other. If a server is storing data, such as a website, and your browser makes a request to download the code for that site, that was the API in action.

Let us look at a more tangible example, and then we'll look at a more real-world, technical one. Suppose you are eating out at a restaurant for dinner. You are equivalent to the client, sitting at the table, and the chef in the back is equivalent to the server.

Since you will never directly talk to the chef, there is no way for him/her to receive your request (for what order you would like to make) or for him/her to provide you with your meal once you order it. We need someone in the middle. In this case, it's the waiter, analogous to the API. The API provides a medium with which you (the client) may talk to the server (the chef), as well as a set of rules for how that communication should be made (the menu — one meal is allowed two sides, etc.)

Now, how do you actually talk to the API (the waiter)? You might speak English, but the chef might speak Spanish. Is the waiter expected to know both languages to translate? What if a third person comes in who only speaks Mandarin? Lalu bagaimana? Well, all clients and servers have to agree to speak a common language, and in computer programming, that language is JSON, pronounced JAY-sun, and it stands for JavaScript Object Notation.

At this point, we don't quite know what JSON looks like. It's not a computer programming language, it's just, well, a language, like English or Spanish, that everyone (everyone being computers) understands on a guaranteed basis. It's guaranteed because it's a standard, notably RFC 8259 , the JavaScript Object Notation (JSON) Data Interchange Format by the Internet Engineering Task Force (IETF).

Even without formal knowledge of what JSON actually is and what it looks like (we'll see in an upcoming article in this series), we can go ahead introduce a technical example operating on the Internet today that employs APIs and JSON. APIs and JSON are not just something you can choose to use, it's not equivalent to one out of a thousand JavaScript frameworks you can pick to do the same thing. It is THE standard for data exchange on the web.

Suppose you are building a travel website that compares prices for aircraft, rental car, and hotel ticket prices. Let us walk through, step-by-step, on a high level, how we would build such an application. Of course, we need our User Interface, the front-end, but that is out of scope for this article.

We want to provide our users with the lowest price booking method. Well, that means we need to somehow attain all possible booking prices, and then compare all of the elements in that set (perhaps we store them in an array) to find the smallest element (known as the infimum in mathematics.)

How will we get this data? Well, suppose all of the booking sites have a database full of prices. Those sites will provide an API, which exposes the data in those databases for use by you. You will call each API for each site to attain all possible booking prices, store them in your own array, find the lowest or minimum element of that array, and then provide the price and booking link to your user. We'll ask the API to query its database for the price in JSON, and it will respond with said price in JSON to us. We can then use, or parse, that accordingly. We have to parse it because APIs will return JSON as a string, not the actual JavaScript data type of JSON. This might not make sense now, and that's okay. We'll be covering it more in a future article.

Also, note that just because something is called an API does not necessarily mean it operates on the web and sends and receives JSON. The Java API, for example, is just the list of classes, packages, and interfaces that are part of the Java Development Kit (JDK), providing programming functionality to the programmer.

Baik. We know we can talk to a program running on a server by way of an Application Programming Interface, and we know that the common language with which we do this is known as JSON. But in the web development and networking world, everything has a protocol. What do we actually do to make an API call, and what does that look like code-wise? That's where HTTP Requests enter the picture, the HyperText Transfer Protocol, defining how messages are formatted and transmitted across the Internet. Once we have an understanding of HTTP (and HTTP verbs, you'll see that in the next section), we can look into actual JavaScript frameworks and methods (like fetch() ) offered by the JavaScript API (similar to the Java API), that actually allow us to make API calls.

HTTP And HTTP Requests

HTTP is the HyperText Transfer Protocol. It is the underlying protocol that determines how messages are formatted as they are transmitted and received across the web. Let's think about what happens when, for example, you attempt to load the home page of Smashing Magazine in your web browser.

You type the website URL (Uniform Resource Locator) in the URL bar, where the DNS server (Domain Name Server, out of scope for this article) resolves the URL into the appropriate IP Address. The browser makes a request, called a GET Request, to the Web Server to, well, GET the underlying HTML behind the site. The Web Server will respond with a message such as “OK”, and then will go ahead and send the HTML down to the browser where it will be parsed and rendered accordingly.

There are a few things to note here. First, the GET Request, and then the “OK” response. Suppose you have a specific database, and you want to write an API to expose that database to your users. Suppose the database contains books the user wants to read (as it will in a future article in this series). Then there are four fundamental operations your user may want to perform on this database, that is, Create a record, Read a record, Update a record, or Delete a record, known collectively as CRUD operations.

Let's look at the Read operation for a moment. Without incorrectly assimilating or conflating the notion of a web server and a database, that Read operation is very similar to your web browser attempting to get the site from the server, just as to read a record is to get the record from the database.

Ini dikenal sebagai Permintaan HTTP. Anda membuat permintaan ke beberapa server di suatu tempat untuk mendapatkan beberapa data, dan, dengan demikian, permintaan tersebut dengan tepat dinamai "GET", kapitalisasi menjadi cara standar untuk menunjukkan permintaan tersebut.

Bagaimana dengan bagian Create dari CRUD? Nah, ketika berbicara tentang Permintaan HTTP, itu dikenal sebagai permintaan POST. Sama seperti Anda mungkin memposting pesan di platform media sosial, Anda mungkin juga memposting catatan baru ke database.

Pembaruan CRUD memungkinkan kita untuk menggunakan Permintaan PUT atau PATCH untuk memperbarui sumber daya. PUT HTTP akan membuat catatan baru atau akan memperbarui/mengganti yang lama.

Mari kita lihat ini sedikit lebih detail, dan kemudian kita akan sampai ke PATCH.

API umumnya bekerja dengan membuat permintaan HTTP ke rute tertentu di URL. Misalkan kita membuat API untuk berbicara dengan DB yang berisi daftar buku pengguna. Kemudian kita mungkin dapat melihat buku-buku itu di URL .../books . Permintaan POST ke .../books akan membuat buku baru dengan properti apa pun yang Anda tentukan (pikirkan id, judul, ISBN, penulis, data penerbitan, dll.) di rute .../books . Tidak masalah apa struktur data dasarnya yang menyimpan semua buku di .../books sekarang. Kami hanya peduli bahwa API mengekspos titik akhir itu (diakses melalui rute) untuk memanipulasi data. Kalimat sebelumnya adalah kuncinya: Permintaan POST membuat buku baru di ...books/ route. Perbedaan antara PUT dan POST, kemudian, adalah bahwa PUT akan membuat buku baru (seperti halnya POST) jika tidak ada buku seperti itu, atau, akan menggantikan buku yang sudah ada jika buku sudah ada dalam struktur data yang disebutkan di atas.

Misalkan setiap buku memiliki properti berikut: id, judul, ISBN, penulis, hasRead (boolean).

Kemudian untuk menambahkan buku baru, seperti yang terlihat sebelumnya, kita akan membuat permintaan POST ke .../books . Jika kami ingin memperbarui atau mengganti buku sepenuhnya, kami akan membuat permintaan PUT ke .../books/id di mana id adalah ID buku yang ingin kami ganti.

Sementara PUT sepenuhnya menggantikan buku yang ada, PATCH memperbarui sesuatu yang berkaitan dengan buku tertentu, mungkin memodifikasi properti boolean hasRead yang kami definisikan di atas — jadi kami akan membuat permintaan PATCH ke …/books/id mengirimkan data baru.

Mungkin sulit untuk melihat arti dari ini sekarang, karena sejauh ini, kami telah menetapkan semuanya secara teori tetapi belum melihat kode nyata yang benar-benar membuat permintaan HTTP. Namun, kami akan segera membahasnya, mencakup GET di artikel ini, dan sisanya di artikel mendatang.

Ada satu operasi CRUD mendasar terakhir dan itu disebut Hapus. Seperti yang Anda harapkan, nama Permintaan HTTP tersebut adalah “DELETE”, dan cara kerjanya hampir sama dengan PATCH, yang mengharuskan ID buku diberikan dalam sebuah rute.

Kami telah belajar sejauh ini, bahwa rute adalah URL spesifik tempat Anda membuat Permintaan HTTP, dan bahwa titik akhir adalah fungsi yang disediakan API, melakukan sesuatu pada data yang dieksposnya. Artinya, titik akhir adalah fungsi bahasa pemrograman yang terletak di ujung lain rute, dan menjalankan Permintaan HTTP apa pun yang Anda tentukan. Kami juga mengetahui bahwa ada istilah seperti POST, GET, PUT, PATCH, DELETE, dan lainnya (dikenal sebagai kata kerja HTTP) yang sebenarnya menentukan permintaan apa yang Anda buat ke API. Seperti JSON, Metode Permintaan HTTP ini adalah standar Internet seperti yang didefinisikan oleh Internet Engineering Task Force (IETF), terutama, RFC 7231, Bagian Empat: Metode Permintaan, dan RFC 5789, Bagian Dua: Metode Patch, di mana RFC adalah akronim untuk Permintaan untuk Komentar.

Jadi, kita mungkin membuat permintaan GET ke URL .../books/id di mana ID yang diteruskan dikenal sebagai parameter. Kita dapat membuat permintaan POST, PUT, atau PATCH ke .../books untuk membuat sumber daya atau ke .../books/id untuk memodifikasi/mengganti/memperbarui sumber daya. Dan kami juga dapat membuat permintaan DELETE ke .../books/id untuk menghapus buku tertentu.

Daftar lengkap Metode Permintaan HTTP dapat ditemukan di sini.

Penting juga untuk dicatat bahwa setelah membuat Permintaan HTTP, kami akan menerima tanggapan. Respons spesifik ditentukan oleh cara kami membangun API, tetapi Anda harus selalu menerima kode status. Sebelumnya, kami mengatakan bahwa ketika browser web Anda meminta HTML dari server web, itu akan merespons dengan "OK". Itu dikenal sebagai Kode Status HTTP, lebih khusus lagi, HTTP 200 OK. Kode status hanya menentukan bagaimana operasi atau tindakan yang ditentukan di titik akhir (ingat, itulah fungsi kami yang melakukan semua pekerjaan) selesai. Kode Status HTTP dikirim kembali oleh server, dan mungkin banyak yang Anda kenal, seperti 404 Not Found (sumber daya atau file tidak dapat ditemukan, ini seperti membuat permintaan GET ke .../books/id di mana tidak ada ID seperti itu.)

Daftar lengkap Kode Status HTTP dapat ditemukan di sini.

MongoDB

MongoDB adalah database NoSQL non-relasional yang mirip dengan Firebase Real-time Database. Anda akan berbicara dengan database melalui paket Node seperti MongoDB Native Driver atau Mongoose.

Di MongoDB, data disimpan dalam JSON, yang cukup berbeda dari database relasional seperti MySQL, PostgreSQL, atau SQLite. Keduanya disebut database, dengan SQL Tables disebut Collections, SQL Table Rows disebut Documents, dan SQL Table Columns disebut Fields.

Kami akan menggunakan Basis Data MongoDB dalam artikel mendatang dalam seri ini saat kami membuat API Rak Buku pertama kami. Operasi CRUD mendasar yang tercantum di atas dapat dilakukan pada Basis Data MongoDB.

Anda disarankan untuk membaca Dokumen MongoDB untuk mempelajari cara membuat database langsung di Cluster Atlas dan membuat Operasi CRUD dengan MongoDB Native Driver. Pada artikel berikutnya dari seri ini, kita akan mempelajari cara menyiapkan database lokal dan database produksi cloud.

Membangun Aplikasi Node Baris Perintah

Saat membuat aplikasi, Anda akan melihat banyak penulis membuang seluruh basis kode mereka di awal artikel, dan kemudian mencoba menjelaskan setiap baris setelahnya. Dalam teks ini, saya akan mengambil pendekatan yang berbeda. Saya akan menjelaskan kode saya baris demi baris, membangun aplikasi sambil berjalan. Saya tidak akan khawatir tentang modularitas atau kinerja, saya tidak akan membagi basis kode menjadi file terpisah, dan saya tidak akan mengikuti Prinsip KERING atau mencoba membuat kode dapat digunakan kembali. Ketika baru belajar, ada baiknya membuat hal-hal sesederhana mungkin, dan itulah pendekatan yang akan saya ambil di sini.

Mari kita menjadi jelas tentang apa yang kita sedang membangun. Kami tidak akan peduli dengan input pengguna, jadi kami tidak akan menggunakan paket seperti Yargs. Kami juga tidak akan membangun API kami sendiri. Itu akan muncul di artikel selanjutnya dalam seri ini ketika kita menggunakan Express Web Application Framework. Saya mengambil pendekatan ini agar tidak menggabungkan Node.js dengan kekuatan Express dan API karena sebagian besar tutorial melakukannya. Sebaliknya, saya akan memberikan satu metode (dari banyak) yang digunakan untuk memanggil dan menerima data dari API eksternal yang menggunakan perpustakaan JavaScript pihak ketiga. API yang akan kita panggil adalah Weather API, yang akan kita akses dari Node dan membuang outputnya ke terminal, mungkin dengan beberapa format, yang dikenal sebagai "pretty-printing". Saya akan membahas seluruh proses, termasuk cara menyiapkan API dan mendapatkan Kunci API, yang langkah-langkahnya memberikan hasil yang benar per Januari 2019.

Kami akan menggunakan OpenWeatherMap API untuk proyek ini, jadi untuk memulai, navigasikan ke halaman pendaftaran OpenWeatherMap dan buat akun dengan formulir. Setelah masuk, temukan item menu Kunci API di halaman dasbor (terletak di sini). Jika Anda baru saja membuat akun, Anda harus memilih nama untuk Kunci API Anda dan tekan "Generate". Diperlukan setidaknya 2 jam agar Kunci API baru Anda berfungsi dan dikaitkan dengan akun Anda.

Sebelum kita mulai membangun aplikasi, kita akan mengunjungi Dokumentasi API untuk mempelajari cara memformat Kunci API kita. Dalam proyek ini, kami akan menentukan kode pos dan kode negara untuk mendapatkan informasi cuaca di lokasi tersebut.

Dari dokumen, kita dapat melihat bahwa metode yang kita lakukan adalah dengan memberikan URL berikut:

 api.openweathermap.org/data/2.5/weather?zip={zip code},{country code}

Di mana kita bisa memasukkan data:

 api.openweathermap.org/data/2.5/weather?zip=94040,us

Sekarang, sebelum kita benar-benar dapat memperoleh data yang relevan dari API ini, kita harus menyediakan Kunci API baru kita sebagai parameter kueri:

 api.openweathermap.org/data/2.5/weather?zip=94040,us&appid={YOUR_API_KEY}

Untuk saat ini, salin URL tersebut ke tab baru di browser web Anda, ganti placeholder {YOUR_API_KEY} dengan Kunci API yang Anda peroleh sebelumnya saat mendaftar untuk sebuah akun.

Teks yang dapat Anda lihat sebenarnya adalah JSON — bahasa web yang disepakati seperti yang dibahas sebelumnya.

Untuk memeriksa ini lebih lanjut, tekan Ctrl + Shift + I di Google Chrome untuk membuka alat Pengembang Chrome, lalu navigasikan ke tab Jaringan. Saat ini, seharusnya tidak ada data di sini.

Tab Jaringan Alat Pengembang Chrome Kosong
Alat Pengembang Google Chrome yang kosong (Pratinjau besar)

Untuk benar-benar memantau data jaringan, memuat ulang halaman, dan melihat tab diisi dengan informasi yang berguna. Klik link pertama seperti yang digambarkan pada gambar di bawah ini.

Tab Jaringan Alat Pengembang Chrome Terisi
Alat Pengembang Google Chrome yang terisi (Pratinjau besar)

Setelah Anda mengeklik tautan itu, kami sebenarnya dapat melihat informasi spesifik HTTP, seperti header. Header dikirim sebagai respons dari API (Anda juga dapat, dalam beberapa kasus, mengirim header Anda sendiri ke API, atau Anda bahkan dapat membuat header kustom Anda sendiri (seringkali diawali dengan x- ) untuk dikirim kembali saat membangun API Anda sendiri ), dan hanya berisi informasi tambahan yang mungkin dibutuhkan klien atau server.

Dalam hal ini, Anda dapat melihat bahwa kami membuat Permintaan GET HTTP ke API, dan itu merespons dengan Status HTTP 200 OK. Anda juga dapat melihat bahwa data yang dikirim kembali berada di JSON, seperti yang tercantum di bawah bagian “Response Header”.

Google Dev Tools yang menggambarkan Header dari respons API
Header dalam respons dari API (Pratinjau besar)

Jika Anda menekan tab pratinjau, Anda sebenarnya dapat melihat JSON sebagai Objek JavaScript. Versi teks yang dapat Anda lihat di browser adalah string, karena JSON selalu dikirim dan diterima di seluruh web sebagai string. Itu sebabnya kami harus mengurai JSON dalam kode kami, untuk membuatnya menjadi format yang lebih mudah dibaca — dalam hal ini (dan hampir di setiap kasus) — Objek JavaScript.

Anda juga dapat menggunakan Ekstensi Google Chrome “JSON View” untuk melakukan ini secara otomatis.

Untuk mulai membangun aplikasi kita, saya akan membuka terminal dan membuat direktori root baru dan kemudian cd ke dalamnya. Setelah masuk, saya akan membuat file app.js baru, jalankan npm init untuk menghasilkan file package.json dengan pengaturan default, lalu buka Visual Studio Code.

 mkdir command-line-weather-app && cd command-line-weather-app touch app.js npm init code .

Setelah itu, saya akan mengunduh Axios, memverifikasi bahwa itu telah ditambahkan ke file package.json saya, dan perhatikan bahwa folder node_modules telah berhasil dibuat.

Di browser, Anda dapat melihat bahwa kami membuat Permintaan GET secara manual dengan mengetikkan URL yang sesuai secara manual ke dalam Bilah URL. Axios adalah apa yang memungkinkan saya melakukan itu di dalam Node.js.

Mulai sekarang, semua kode berikut akan ditempatkan di dalam file app.js , setiap cuplikan ditempatkan satu demi satu.

Hal pertama yang akan saya lakukan adalah meminta paket Axios yang kami instal sebelumnya dengan

 const axios = require('axios');

Kami sekarang memiliki akses ke Axios, dan dapat membuat Permintaan HTTP yang relevan, melalui konstanta axios .

Umumnya, panggilan API kami akan dinamis — dalam hal ini, kami mungkin ingin memasukkan kode pos dan kode negara yang berbeda ke dalam URL kami. Jadi, saya akan membuat variabel konstan untuk setiap bagian URL, dan kemudian menggabungkannya dengan ES6 Template Strings. Pertama, kami memiliki bagian dari URL kami yang tidak akan pernah berubah serta Kunci API kami:

 const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here';

Saya juga akan menetapkan kode pos dan kode negara kami. Karena kami tidak mengharapkan input pengguna dan agak sulit mengkodekan data, saya akan membuat ini konstan juga, meskipun, dalam banyak kasus, akan lebih berguna untuk menggunakan let .

 const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us';

Sekarang kita perlu menggabungkan variabel-variabel ini ke dalam satu URL yang dapat kita gunakan Axios untuk membuat Permintaan GET untuk:

 const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`;

Berikut adalah isi file app.js kami hingga saat ini:

 const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`;

Yang tersisa untuk dilakukan adalah benar-benar menggunakan axios untuk membuat Permintaan GET ke URL itu. Untuk itu, kita akan menggunakan metode get(url) yang disediakan oleh axios .

 axios.get(ENTIRE_API_URL)

axios.get(...) sebenarnya mengembalikan Janji, dan fungsi panggilan balik yang berhasil akan menerima argumen respons yang memungkinkan kita mengakses respons dari API — hal yang sama yang Anda lihat di browser. Saya juga akan menambahkan klausa .catch() untuk menangkap kesalahan apa pun.

 axios.get(ENTIRE_API_URL) .then(response => console.log(response)) .catch(error => console.log('Error', error));

Jika sekarang kami menjalankan kode ini dengan node app.js di terminal, Anda akan dapat melihat respons lengkap yang kami dapatkan kembali. Namun, misalkan Anda hanya ingin melihat suhu untuk kode pos itu — maka sebagian besar data dalam respons itu tidak berguna bagi Anda. Axios sebenarnya mengembalikan respons dari API di objek data, yang merupakan properti respons. Itu berarti respons dari server sebenarnya terletak di response.data , jadi mari kita cetak itu di fungsi panggilan balik: console.log(response.data) .

Sekarang, kami mengatakan bahwa server web selalu menangani JSON sebagai string, dan itu benar. Namun, Anda mungkin memperhatikan bahwa response.data sudah menjadi objek (jelas dengan menjalankan console.log(typeof response.data) ) — kami tidak harus menguraikannya dengan JSON.parse() . Itu karena Axios sudah mengurus ini untuk kita di belakang layar.

Output di terminal dari menjalankan console.log(response.data) dapat diformat — “pretty-printed” — dengan menjalankan console.log(JSON.stringify(response.data, undefined, 2)) . JSON.stringify() mengonversi objek JSON menjadi string, dan mengambil objek, filter, dan jumlah karakter yang digunakan untuk indentasi saat mencetak. Anda dapat melihat respons yang diberikan:

 { "coord": { "lon": -118.24, "lat": 33.97 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "base": "stations", "main": { "temp": 288.21, "pressure": 1022, "humidity": 15, "temp_min": 286.15, "temp_max": 289.75 }, "visibility": 16093, "wind": { "speed": 2.1, "deg": 110 }, "clouds": { "all": 1 }, "dt": 1546459080, "sys": { "type": 1, "id": 4361, "message": 0.0072, "country": "US", "sunrise": 1546441120, "sunset": 1546476978 }, "id": 420003677, "name": "Lynwood", "cod": 200 }

Sekarang, jelas untuk melihat bahwa suhu yang kita cari terletak di properti main dari objek response.data , sehingga kita dapat mengaksesnya dengan memanggil response.data.main.temp . Mari kita lihat kode aplikasi hingga sekarang:

 const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`; axios.get(ENTIRE_API_URL) .then(response => console.log(response.data.main.temp)) .catch(error => console.log('Error', error));

Suhu yang kita dapatkan sebenarnya dalam Kelvin, yang merupakan skala suhu yang umumnya digunakan dalam Fisika, Kimia, dan Termodinamika karena fakta bahwa ia memberikan titik "nol mutlak", yang merupakan suhu di mana semua gerakan termal semua bagian dalam partikel berhenti. Kita hanya perlu mengubahnya ke Fahrenheit atau Celcius dengan rumus di bawah ini:

F = K * 9/5 - 459,67
C = K - 273,15

Mari kita perbarui callback sukses kita untuk mencetak data baru dengan konversi ini. Kami juga akan menambahkan kalimat yang tepat untuk tujuan Pengalaman Pengguna:

 axios.get(ENTIRE_API_URL) .then(response => { // Getting the current temperature and the city from the response object. const kelvinTemperature = response.data.main.temp; const cityName = response.data.name; const countryName = response.data.sys.country; // Making K to F and K to C conversions. const fahrenheitTemperature = (kelvinTemperature * 9/5) — 459.67; const celciusTemperature = kelvinTemperature — 273.15; // Building the final message. const message = ( `Right now, in \ ${cityName}, ${countryName} the current temperature is \ ${fahrenheitTemperature.toFixed(2)} deg F or \ ${celciusTemperature.toFixed(2)} deg C.`.replace(/\s+/g, ' ') ); console.log(message); }) .catch(error => console.log('Error', error));

Tanda kurung di sekitar variabel message tidak diperlukan, mereka hanya terlihat bagus — mirip dengan saat bekerja dengan JSX di React. Garis miring terbalik menghentikan string templat dari memformat baris baru, dan metode prototipe replace() String menghilangkan ruang putih menggunakan Regular Expressions (RegEx). Metode prototipe Nomor toFixed() float ke sejumlah tempat desimal tertentu — dalam hal ini, dua.

Dengan itu, app.js terakhir kami terlihat sebagai berikut:

 const axios = require('axios'); // API specific settings. const API_URL = 'https://api.openweathermap.org/data/2.5/weather?zip='; const API_KEY = 'Your API Key Here'; const LOCATION_ZIP_CODE = '90001'; const COUNTRY_CODE = 'us'; const ENTIRE_API_URL = `${API_URL}${LOCATION_ZIP_CODE},${COUNTRY_CODE}&appid=${API_KEY}`; axios.get(ENTIRE_API_URL) .then(response => { // Getting the current temperature and the city from the response object. const kelvinTemperature = response.data.main.temp; const cityName = response.data.name; const countryName = response.data.sys.country; // Making K to F and K to C conversions. const fahrenheitTemperature = (kelvinTemperature * 9/5) — 459.67; const celciusTemperature = kelvinTemperature — 273.15; // Building the final message. const message = ( `Right now, in \ ${cityName}, ${countryName} the current temperature is \ ${fahrenheitTemperature.toFixed(2)} deg F or \ ${celciusTemperature.toFixed(2)} deg C.`.replace(/\s+/g, ' ') ); console.log(message); }) .catch(error => console.log('Error', error));

Kesimpulan

Kami telah belajar banyak tentang cara kerja Node dalam artikel ini, mulai dari perbedaan antara permintaan sinkron dan asinkron, hingga fungsi panggilan balik, hingga fitur ES6 baru, peristiwa, manajer paket, API, JSON, dan Protokol Transfer HyperText, Basis Data Non-Relasional , dan kami bahkan membangun aplikasi baris perintah kami sendiri dengan memanfaatkan sebagian besar pengetahuan yang baru ditemukan itu.

Di artikel mendatang dalam seri ini, kita akan melihat secara mendalam Call Stack, Event Loop, dan Node API, kita akan berbicara tentang Cross-Origin Resource Sharing (CORS), dan kita akan membangun Full Stack Bookshelf API memanfaatkan database, titik akhir, autentikasi pengguna, token, rendering template sisi server, dan banyak lagi.

Dari sini, mulailah membangun aplikasi Node Anda sendiri, baca dokumentasi Node, keluar dan temukan API atau Modul Node yang menarik dan implementasikan sendiri. Dunia adalah tiram Anda dan Anda memiliki akses di ujung jari Anda ke jaringan pengetahuan terbesar di planet ini — Internet. Gunakan untuk keuntungan Anda.

Bacaan Lebih Lanjut tentang SmashingMag:

  • Memahami Dan Menggunakan REST API
  • Fitur JavaScript Baru Yang Akan Mengubah Cara Anda Menulis Regex
  • Menjaga Node.js Cepat: Alat, Teknik, Dan Tips Untuk Membuat Server Node.js Berkinerja Tinggi
  • Membangun Chatbot AI Sederhana Dengan Web Speech API Dan Node.js