Search by

    Terakhir diperbaharui: Oct 24, 2020

    Error Handling

    Sebuah aplikasi tidak mungkin lepas dari galat atau error, termasuk aplikasi DinoTes yang sedang kita buat saat ini.

    Meskipun aplikasi ini cukup sederhana, error terutama saat proses pengembangan(development) pasti akan terjadi dan tidak bisa dihindari.

    Oleh karena itu kita perlu menambahkan error handler.

    Keberadaan error handler ini tidak hanya penting untuk seorang developer namun juga untuk user.

    REST API yang kita buat saat ini belum memiliki error handler.

    Jika terjadi error seperti terputusnya koneksi ke database atau gagal menyimpan notes ke dalam database, maka REST API akan mati dan package Nodemon akan berusaha menghidupkannya kembali.

    Meskipun akhirnya tetap bisa digunakan tapi kita tidak tahu sebenarnya apa yang terjadi, apa yang membuat REST API mati atau crash.

    Padahal mengetahui detail dari error sangatlah penting untuk proses development.

    Sedangkan di sisi user, ketika terjadi error seperti gagal menyimpan data, user tidak menerima feedback apapun.

    Yang akan membuat user tidak mengetahui apa yang sebenarnya terjadi dengan aplikasinya.

    Tentunya hal ini harus dihindari.

    Menambahkan Error Handler

    Seperti yang sudah disebutkan sebelumnya kita bisa membuat sebuah middleware untuk menghandle error yang terjadi di sebuah aplikasi Express.js.

    Kita akan membuat sebuah custom error handler middleware sederhana dengan rincian:

    • REST API akan melakukan pengecekan terhadap data title yang dikirim dari client
    • Jika data tersebut kosong, maka dianggap sebagai sebuah error
    • setiap terjadi error maka server akan mengirimkan informasi dalam bentuk JSON ke client yang berisi kode HTTP response 400 dan pesan bahwa terjadi sebuah galat / error
    • print detail dari error message di console, untuk development

    Untuk daftar lengkap kode HTTP response yang bisa digunakan, silahkan lihat disini.

    Step by step

    1. Buat sebuah folder dengan nama middlewares di dalam folder api, folder middleware ini digunakan untuk menyimpan semua custom middleware yang kita buat

    Struktur Folder:

    1dinotes-app
    2 |--api
    3 |--middlewares
    4 |--node_modules
    5 |--public
    6 |--src
    1. Buat file baru dengan nama errorHandler.js, lalu salin code berikut ini:
    1// eslint-disable-next-line no-unused-vars
    2const handleErrors = (err, req, res, next) => {
    3 return res.status(500).json({
    4 status: 'error',
    5 message: err.message
    6 });
    7};
    8
    9module.exports = handleErrors;
    1. Register/load middleware di file api/server.js
    1const express = require('express');
    2const { MongoClient } = require('mongodb');
    3const bodyParser = require('body-parser');
    4
    5const routes = require('./routes');
    6const handleErrors = require('./middlewares/errorHandler');
    7
    8const app = express();
    9const port = 3001;
    10
    11// Connection URL
    12const url = 'mongodb://localhost:27017';
    13// Database Name
    14const dbName = 'dinotesDB';
    15
    16app.use(bodyParser.urlencoded({ extended: true }));
    17
    18// Connect to Database
    19
    20MongoClient.connect(url, (err, client) => {
    21 const db = client.db(dbName);
    22 const notesCollection = db.collection('notes');
    23
    24 app.locals.notesCollection = notesCollection;
    25});
    26
    27// Routes
    28
    29app.use('/', routes);
    30
    31app.use(handleErrors);
    32
    33app.listen(port, () => {
    34 console.log(`Server listening at http://localhost:${port}`);
    35});
    1. Update handler

    Selanjutnya kita update handler addNote agar dapat menghandle error jika data title tidak disediakan oleh client.

    Kita tambahkan try catch statement untuk menangkap error yang terjadi untuk selanjutnya kita teruskan ke Express.js.

    Sebelum itu kita harus mengubah handler dari menggunakan method then() menjadi async/await.

    Output dari query database menggunakan Node.js MongoDB driver adalah sebuah promise maka kita bisa mengubahnya menjadi async await.

    Setiap error yang diteruskan ke Express.js akan dihandle oleh custom error handler middleware yang sudah dibuat sebelumnya.

    1...
    2
    3exports.addNote = async (req, res, next) => {
    4 const { notesCollection } = req.app.locals;
    5 const { title } = req.body;
    6
    7 try {
    8 if (!title) {
    9 throw new Error('title is missing');
    10 }
    11 // Insert data to collection
    12 const result = await notesCollection.insertOne(req.body);
    13
    14 res.status(200).json(result);
    15 } catch (error) {
    16 next(error);
    17 }
    18
    19 res.status(200).json('Data successfully saved');
    20};
    21
    22...

    Testing

    Sekarang API tidak membolehkan client untuk mengirim data tanpa title.

    Jika kita tetap melakukannya, maka kita akan mendapat pesan kesalahan dalam bentuk JSON:

    • Postman

    error handler express

    Kita bisa lanjutkan untuk update semua handler agar bisa menghandle error yang terjadi, tapi error yang dihandle bersifat umum.

    api/handler.js

    1const { ObjectId } = require('mongodb');
    2
    3exports.addNote = async (req, res, next) => {
    4 const { notesCollection } = req.app.locals;
    5 const { title } = req.body;
    6
    7 try {
    8 if (!title) {
    9 throw new Error('title is missing');
    10 }
    11 // Insert data to collection
    12 const result = await notesCollection.insertOne(req.body);
    13
    14 console.log(result);
    15
    16 res.status(200).json('Data successfully saved');
    17 } catch (error) {
    18 next(error);
    19 }
    20};
    21
    22exports.getAllNotes = async (req, res, next) => {
    23 const { notesCollection } = req.app.locals;
    24
    25 try {
    26 // find all Notes
    27 const result = await notesCollection.find().toArray();
    28
    29 res.status(200).json(result);
    30 } catch (error) {
    31 next(error);
    32 }
    33};
    34
    35exports.getNote = async (req, res, next) => {
    36 const { notesCollection } = req.app.locals;
    37
    38 try {
    39 // find Notes based on id
    40 const result = await notesCollection.findOne({ _id: ObjectId(req.params.id) });
    41
    42 res.status(200).json(result);
    43 } catch (error) {
    44 next(error);
    45 }
    46};
    47
    48exports.updateNote = async (req, res, next) => {
    49 const { notesCollection } = req.app.locals;
    50
    51 try {
    52 // update data collection
    53 const result = await notesCollection.updateOne(
    54 { _id: ObjectId(req.params.id) },
    55 { $set: { title: req.body.title, note: req.body.note } }
    56 );
    57
    58 console.log(result);
    59
    60 res.status(200).json('Data successfully updated');
    61 } catch (error) {
    62 next(error);
    63 }
    64};
    65
    66exports.deleteNote = async (req, res, next) => {
    67 const { notesCollection } = req.app.locals;
    68
    69 try {
    70 // delete data collection
    71 const result = await notesCollection.deleteOne({ _id: ObjectId(req.params.id) });
    72 console.log(result);
    73 res.status(200).json('Data successfully deleted');
    74 } catch (error) {
    75 next(error);
    76 }
    77};