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
- 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-app2 |--api3 |--middlewares4 |--node_modules5 |--public6 |--src
- Buat file baru dengan nama errorHandler.js, lalu salin code berikut ini:
1// eslint-disable-next-line no-unused-vars2const handleErrors = (err, req, res, next) => {3 return res.status(500).json({4 status: 'error',5 message: err.message6 });7};89module.exports = handleErrors;
- Register/load middleware di file api/server.js
1const express = require('express');2const { MongoClient } = require('mongodb');3const bodyParser = require('body-parser');45const routes = require('./routes');6const handleErrors = require('./middlewares/errorHandler');78const app = express();9const port = 3001;1011// Connection URL12const url = 'mongodb://localhost:27017';13// Database Name14const dbName = 'dinotesDB';1516app.use(bodyParser.urlencoded({ extended: true }));1718// Connect to Database1920MongoClient.connect(url, (err, client) => {21 const db = client.db(dbName);22 const notesCollection = db.collection('notes');2324 app.locals.notesCollection = notesCollection;25});2627// Routes2829app.use('/', routes);3031app.use(handleErrors);3233app.listen(port, () => {34 console.log(`Server listening at http://localhost:${port}`);35});
- 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...23exports.addNote = async (req, res, next) => {4 const { notesCollection } = req.app.locals;5 const { title } = req.body;67 try {8 if (!title) {9 throw new Error('title is missing');10 }11 // Insert data to collection12 const result = await notesCollection.insertOne(req.body);1314 res.status(200).json(result);15 } catch (error) {16 next(error);17 }1819 res.status(200).json('Data successfully saved');20};2122...
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
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');23exports.addNote = async (req, res, next) => {4 const { notesCollection } = req.app.locals;5 const { title } = req.body;67 try {8 if (!title) {9 throw new Error('title is missing');10 }11 // Insert data to collection12 const result = await notesCollection.insertOne(req.body);1314 console.log(result);1516 res.status(200).json('Data successfully saved');17 } catch (error) {18 next(error);19 }20};2122exports.getAllNotes = async (req, res, next) => {23 const { notesCollection } = req.app.locals;2425 try {26 // find all Notes27 const result = await notesCollection.find().toArray();2829 res.status(200).json(result);30 } catch (error) {31 next(error);32 }33};3435exports.getNote = async (req, res, next) => {36 const { notesCollection } = req.app.locals;3738 try {39 // find Notes based on id40 const result = await notesCollection.findOne({ _id: ObjectId(req.params.id) });4142 res.status(200).json(result);43 } catch (error) {44 next(error);45 }46};4748exports.updateNote = async (req, res, next) => {49 const { notesCollection } = req.app.locals;5051 try {52 // update data collection53 const result = await notesCollection.updateOne(54 { _id: ObjectId(req.params.id) },55 { $set: { title: req.body.title, note: req.body.note } }56 );5758 console.log(result);5960 res.status(200).json('Data successfully updated');61 } catch (error) {62 next(error);63 }64};6566exports.deleteNote = async (req, res, next) => {67 const { notesCollection } = req.app.locals;6869 try {70 // delete data collection71 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};