Dicoding Submission Bookshelf API - Belajar Membuat Aplikasi Back-End untuk Pemula


Berikut adalah panduan untuk membuat project "Submission Bookshelf API" dalam kelas "Belajar Membuat Aplikasi Back-End untuk Pemula".

1. Pastikan telah menginstal Nodejs di komputer anda. Selanjutnya, langkah yang perlu dilakukan   adalah membuat direktori khusus untuk project tersebut.

2. Buka command prompt di dalam direktori tersebut, kemudian ketik perintah berikut untuk membuat project node.js.


npm init --y

3. Buatlah direktori baru bernama src di dalam direktori project tersebut. Selanjutnya, buatlah file dengan nama server.js, routes.js, handler.js dan items.js.

4. Untuk memudahkan pengembangan, disarankan menggunakan tools nodemon. Agar tidak perlu melakukan ulang server secara manual setiap kali terjadi perubahan pada kode JavaScript, karena Nodemon dapat mendeteksi perubahan tersebut dan mengeksekusi ulang server secara otomatis. Untuk menggunakan Nodemon, jalankan perintah berikut. 


npm install nodemon --save-dev


5. Dalam berkas package.json, di bagian scripts, tambahkan kode start-dev digunakan untuk menjalankan project pada tahap pengembangan.


  1
  2
  3
  4
  5
  6
  // package.json
  "scripts": {     
    "start": "node ./src/server.js",                                                     
    "start-dev": "nodemon ./src/server.js",                                              
    "test": "echo \"Error: no test specified\" && exit 1"
  },

6. Silahkan jalankan perintah npm run start-dev untuk memulai project pada tahap pengembangan. 

7. ESLint berperan penting dalam membantu kode javaScript dengan konsisten gaya yang baik. Silahkan jalankan perintah di bawah ini untuk menginstal EsLint dan menyimpannya sebagai bagian dari dev Dependencies di berkas package.json. 


npm install eslint --save-dev


8. Jalankan perintah berikut untuk mengkonfigurasi ESLint.


npx eslint --init


Selanjutnya, jawab pertanyaan yang muncul dengan menggunakan 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 does your project use ? -> None of these.
  • Does your project use TypeScript ? -> No.
  • Where does your code run ? -> browser (Tekan spasi untuk memilih).
  • How would you like to define a style for your project ? -> Use a popular style guide.
  • Which style guide do you want to follow ? -> Airbnb: https://github.com/airbnb/javascript.
  • What format do you want your config file to be in ? -> JSON.
  • Would you like to install them now ? -> Y.
  • Which package manager do you want to use ? -> npm. 
9. Tambahkan script berikut ke dalam bagian scripts pada berkas package.json :


 1
 2
 3
 4
 5
 6
 7
 // package.json
  "scripts": {     
    "start": "node ./src/server.js",
    "start-dev": "nodemon ./src/server.js",
    "lint": "eslint ./src",                                                          
    "test": "echo \"Error: no test specified\" && exit 1"
  },






10. Struktur project seharunya terlihat seperti ini .


11. Lalu, install extension ESLint pada Visual Studio Code untuk memudahkan integrasinya dengan text editor.

12. Selanjutnya, install HTTP server menggunakan Hapi.


npm install @hapi/hapi


13. Pada server.js, tambahkan code berikut ini. 


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 // server.js
 const Hapi = require('@hapi/hapi');

 const init = async () => {
   const server = Hapi.server({
     port: 9000,
     host: 'localhost',
   });

   await server.start();

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

 init();


14. Pada routes.js, tambahkan code berikut.

 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 // routes.js
 const routes = [
   {
     method: 'POST',
     path: '/books',
     handler: () => {}, // Bagian ini, nantinya akan ditambahkan dengan handler
   },
 ];

 module.exports = routes;

15.  Hubungkan routes.js ke server.js.

 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
    const Hapi = require('@hapi/hapi');
const routes = require('./routes');                                                         
const init = async () => {
const server = Hapi.server({ port: 9000, host: 'localhost', }); server.route(routes);                                                          await server.start(); console.log(`Server berjalan pada ${server.info.uri}`); }; init();



 
   
   

 
 

 


16. Untuk mengatasi Same-Origin Policy, tambahkan code berikut di dalam file server.js.


  1
  2
  3
  4
  5 
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 // server.js
 const Hapi = require('@hapi/hapi');
 const routes = require('./routes');

const init = async () => {
  const server = Hapi.server({
    port: 9000,
    host: 'localhost',
    routes: {                                                                           
      cors: {                                                                           
        origin: ['*'],                                                                  
      },                                                                                
    },                                                                                  
  });

  server.route(routes);
  await server.start();

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

init();

17. Pada items.js, tambahkan code di bawah ini.


  1
  2
  3
  4
  // items.js
  const items = [];

  module.exports = items;


18. Tambahkan code di bawah ini pada handler.js. Code request.payload berguna untuk merubah payload JSON menjadi object JavaScript.


 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30  
  // handler.js
  const items = require('./items');

  const addItemHandler = (request, h) => {
   const {
     name,
     year,
     author,
     summary,
     publisher,
     pageCount,
     readPage,
     reading,
   } = request.payload;

   const newItem = {
     name,
     year,
     author,
     summary,
     publisher,
     pageCount,
     readPage,
     reading,
   };

   items.push(newItem);
 };

module.exports = { addItemHandler };

19. Tambahkan addltemHandler pada routes.js


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 // routes.js
 const { addItemHandler } = require('./handler');                                        

 const routes = [
   {
     method: 'POST',
     path: '/books',
     handler: addItemHandler,                                                            
   },
 ];

 module.exports = routes;


20. Selanjutnya, install nanoid untuk membuat id buku dengan perintah di bawah in. Gunakan nanoid versi 3.x.x, karena jika menggunakan versi terbaru, nanoid tidak dapat digunakan dengan format modul CommonJS.


npm install nanoid@3.x.x. // x.x bisa diganti dengan versi nanoid yang tersedia.
 

21. Kemudian, tambahkan code respon ketika tidak ada properti boolean yang menjelaskan apakah buku telah selesai dibaca atau belum, dengan properti name pada request body dan nilai properti readPage lebih besar dari pageCount.

  1 
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
// handler.js
const items = require('./items');

const addItemHandler = (request, h) => {
  const {
    name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
    reading,
  } = request.payload;

  if (!name) {                                                                          
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message: 'Gagal menambahkan buku. Mohon isi nama buku',                         
      })                                                                                
      .code(400);                                                                       
    return response                                                                   
  }                                                                                     
                                                                                        
  if (readPage > pageCount) {                                                           
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message:                                                                        
          'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',    
      })                                                                                
      .code(400)                                                                      
    return response;                                                                    
  }                                                                                     

  const newItem = {
    name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
    reading,
  };

  items.push(newItem);
};

module.exports = { addItemHandler };


22. Tambahkan beberapa atribut untuk melengkapi handler.js


  1 
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
// handler.js
const { nanoid } = require('nanoid');                                                   
const items = require('./items');
const addItemHandler = (request, h) => {
  const {
name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
    reading,
  } = request.payload;
if (!name) {                                                                     
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message: 'Gagal menambahkan buku. Mohon isi nama buku',                         
      })                                                                                
      .code(400);                                                                       
    return response                                                                   
  }  
if (readPage > pageCount) {                                                   
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message:                                                                        
          'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',    
      })                                                                                
      .code(400)                                                                      
    return response;                                                                    
  }
const id = nanoid(16);                                                                 const finished = pageCount === readPage;                                               const insertedAt = new Date().toISOString();                                         const updatedAt = insertedAt;                                                         const newItem = { id,                                                                                 name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
finished, reading, insertedAt,                                                                          updatedAt,                                                                         };

  items.push(newItem);
};

module.exports = { addItemHandler };


23. Dengan menambahkan variabel isSuccess untuk mengecek newItem sudah masuk dalam array item menggunakan methid filter(). Jangan lupa tambahkan respon ketika dimana newItem berhasil dan gagal ke array items.

 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 // handler.js
 const { nanoid } = require('nanoid');                                                   
 const items = require('./items');
const addItemHandler = (request, h) => {
  const {
name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
    reading,
  } = request.payload;
if (!name) {                                                                     
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message: 'Gagal menambahkan buku. Mohon isi nama buku',                         
      })                                                                                
      .code(400);                                                                       
    return response                                                                   
  }  
if (readPage > pageCount) {                                                   
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message:                                                                        
          'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',    
      })                                                                                
      .code(400)                                                                      
    return response;                                                                    
  }
const id = nanoid(16);                                                                
  const finished = pageCount === readPage;                                              
  const insertedAt = new Date().toISOString();                                          
  const updatedAt = insertedAt;                                                         

 const newItem = {
    id,                                                                                 
    name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
finished, reading, insertedAt,                                                                          updatedAt,                                                                         };
items.push(newItem);
const isSuccess = items.filter((book) => book.id === id).length > 0; if (isSuccess) { const response = h .response({ status: 'success', message: 'Buku berhasil ditambahkan', data: { bookId: id, }, }) .code(201); return response; } const response = h .response({ status: 'fail', message: 'Buku gagal ditambahkan', }) .code(500); return response; };

 module.exports = { addItemHandler };


24. Kemudian tambahkan handler lainnya sesuai permintaan, pada bagian handler.js


  1 
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
// handler.js
const { nanoid } = require('nanoid');                                                   
const items = require('./items');
const addItemHandler = (request, h) => {
  const {
name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
    reading,
  } = request.payload;
if (!name) {                                                                     
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message: 'Gagal menambahkan buku. Mohon isi nama buku',                         
      })                                                                                
      .code(400);                                                                       
    return response                                                                   
  }  
if (readPage > pageCount) {                                             
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message:                                                                        
          'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',    
      })                                                                                
      .code(400)                                                                      
    return response;                                                                    
  }
const id = nanoid(16);                                                         
  const finished = pageCount === readPage;                                              
  const insertedAt = new Date().toISOString();                                          
  const updatedAt = insertedAt;  
 const newItem = {
    id,                                                                                 
    name,
    year,
    author,
    summary,
    publisher,
    pageCount,
    readPage,
finished, reading, insertedAt,                                                                          updatedAt,                                                                         };
items.push(newItem); const isSuccess = items.filter((book) => book.id === id).length > 0; if (isSuccess) { const response = h .response({ status: 'success', message: 'Buku berhasil ditambahkan', data: { bookId: id, }, }) .code(201); return response; } const response = h .response({ status: 'fail', message: 'Buku gagal ditambahkan', }) .code(500); return response; }; const addItemHandler = (request, h) => {}                                             
                                                                                     const getItemByIdHandler = (request, h) => {}                                                                      const editItemByIdHandler = (request, h) => {} const deleteItemByIdHandler = (request, h) => {}
module.exports = {
addItemHandler, getAllItemsHandler, getItemByIdHandler, editItemByIdHandler, deleteItemByIdHandler, };


25. Selanjutnya menambahkan handler yang telah dibuat pada routes.js sebelumnya.


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 // routes.js
 const {
   addItemHandler,
   getAllItemsHandler,                                                                   
   getItemByIdHandler,                                                                   
   editItemByIdHandler,                                                                  
   deleteItemByIdHandler,                                                                
} = require('./handler'); const routes = [
  {
method: 'POST', path: '/books', handler: addItemHandler, }, {                                                                                      method: 'GET',                                                                     path: '/books',                                                                     handler: getAllItemsHandler,                                                         },                                                                                     {                                                                                      method: 'GET',                                                                     path: '/books/{bookId}',                                                             handler: getItemByIdHandler,                                                         },                                                                                     {                                                                                     method: 'PUT',                                                                       path: '/books/{bookId}',                                                             handler: editItemByIdHandler,                                                     },                                                                                     {                                                                                     method: 'DELETE',                                                                 path: '/books/{bookId}',                                                             handler: deleteItemByIdHandler,                                                      {                                                                                     method: '*',                                                                         path: '/{any*}',                                                                 handler: () => 'Halaman tidak ditemukan',                                         },                                                                                 ]; module.exports = routes;


26. Tambahkan untuk melengkapi bagian getAllltemsHandler. 


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 const getAllItemsHandler = (request, h) => {
   const { name, reading, finished } = request.query;

   // Terdapat query name
   if (name) {
     const filteredBooksName = items.filter((book) => {
       const nameRegex = new RegExp(name, 'gi');
       return nameRegex.test(book.name);
     });

     const response = h
       .response({
         status: 'success',
         data: {
           books: filteredBooksName.map((book) => ({
             id: book.id,
             name: book.name,
             publisher: book.publisher,
           })),
         },
       })
       .code(200);

     return response;
   }

   if (reading) {
     const filteredBooksReading = items.filter(
       (book) => Number(book.reading) === Number(reading),
     );

     const response = h
       .response({
         status: 'success',
         data: {
           books: filteredBooksReading.map((book) => ({
             id: book.id,
             name: book.name,
             publisher: book.publisher,
           })),
         },
       })
       .code(200);

     return response;
   }

   if (finished) {
     const filteredBooksFinished = items.filter(
       (book) => Number(book.finished) === Number(finished),
     );

     const response = h
       .response({
         status: 'success',
         data: {
           books: filteredBooksReading.map((book) => ({
             id: book.id,
             name: book.name,
             publisher: book.publisher,
           })),
         },
       })
       .code(200);
return response; } const response = h
      .response({
        status: 'success',
        data: {
          books: items.map((book) => ({
            id: book.id,
            name: book.name,
            publisher: book.publisher,
          })),
        },
      })
      .code(200);
return response; };


27. Tambahkan bagian getltemByIdHandler. 


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 const getItemByIdHandler = (request, h) => {
   const { bookId } = request.params;
   const book = items.filter((note) => note.id === bookId)[0];

   // Jika mencari book id ditemukan
   if (book) {
     const response = h
       .response({
         status: 'success',
         data: {
book, },
       })
       .code(200);
     return response;
   }
// Jika mencari book id tidak ditemukan const response = h .response({ status: 'fail', message: 'Buku tidak ditemukan', }) .code(200); return response; };


28. Tambahkan bagian editItemByIdHandler


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 const editItemByIdHandler = (request, h) => {
   const { bookId } = request.params;

   const {
     name,
     year,
     author,
     summary,
     publisher,
     pageCount,
     readPage,
     reading,
   } = request.payload;
// Tidak ada properti name pada body if (!name) { const response = h
       .response({
status: 'fail', message: 'Gagal memperbarui buku. Mohon isi nama buku', }) .code(400); return response; } // Nilai properti readPage lebih besar dari pageCount pada body if (readPage > pageCount) {                                             
    const response =                                                                  
      .response({                                                                       
        status: 'fail',                                                                 
        message:                                                                        
          'Gagal menambahkan buku. readPage tidak boleh lebih besar dari pageCount',    
      })                                                                                
      .code(400)                                                                      
    return response;                                                                    
  }
const finished = pageCount === readPage;  const updatedAt = new Date().toISOString();
     const index = items.findIndex((book) => book.id === bookId);

  // Jika book dengan id yang dicari ditemukan
   if (index !== -1) {
     items[index] = {
       ...items[index],
      name,
      year,
      author,
      summary,
      publisher,
      pageCount,
      readPage,
finished, reading, insertedAt,                                                                          updatedAt,                                                                         };
const response = h
      .response({
        status: 'success',
        message: 'Buku berhasil ditambahkan',
}) .code(200); return response; } // Jika tidak menemukan book dengan id. const response = h .response({ status: 'fail', message: 'Gagal memperbarui buku. Id tidak ditemukan', }) .code(404); return response; };


29. Tambahkan bagian deleteItemByIdHandler


  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 const deleteItemByIdHandler = (request, h) => {
   const { bookId } = request.params;

   const index = items.findIndex((book) => book.id === bookId);

   if (index !== -1) {
     items.splice(index, 1);

     const response = h
       .response({
         status: 'success',
         message: 'Buku berhasil dihapus',
       })
       .code(200);
     return response;
   }

   const response = h
     .response({
       status: 'fail',
       message: 'Buku gagal dihapus. Id tidak ditemukan',
     })
     .code(404);
  return response;
  };


30. Jalankan perintah dibawah ini untuk mencoba API handler.


curl -X POST -H "Content-Type: application/json" http://localhost:9000/books -d "{\"name\": \"Sherina\"}"


31. Menguji API handler, jalankan perintah dibawah ini.        

 
curl -X GET -H "Content-Type: application/json" http://localhost:9000/books?name=Sherina

  

   SELESAI !

   GitHub LinkClick Here

Postingan populer dari blog ini

Tutorial membuat aplikasi chatting Client Visual Studio C# dengan menggunakan koneksi TCP

Cara membuat aplikasi Game Threat menggunakan Visual Studio C#