Module 8: Error Handling & Validation 🛡️


1. Error Handling: යෙදුම බිඳ වැටීමෙන් වළක්වා ගැනීම

හොඳින් නිර්මාණය කරන ලද application එකක් සහ සාමාන්‍ය application එකක් අතර ඇති ප්‍රධාන වෙනසක් වන්නේ එය වැරදි සහ අනපේක්ෂිත අවස්ථා (errors) හසුරුවන ආකාරයයි. Error handling නිසියාකාරව සිදු නොකළහොත්, යම් සුළු දෝෂයක් නිසා මුළු server එකම බිඳ වැටීමට (crash) ඉඩ ඇත. මේ නිසා, අපේ API එක ශක්තිමත් (robust) සහ විශ්වාසනීය (reliable) කිරීමට, ක්‍රමවත් error handling අත්‍යවශ්‍ය වේ.

Express හි Default Error Handler

Express සතුව built-in error handler එකක් ඇත. ඔබේ code එකේ යම් තැනක error එකක් ඇති වුවහොත්, Express එය හසුරුවාගෙන client එකට HTML error page එකක් සමඟ 500 (Internal Server Error) status code එකක් යවයි. නමුත් production-level API එකකදී, අපට HTML page එකක් වෙනුවට, JSON format එකෙන්, වඩාත් අර්ථවත් error message එකක් යැවීමට අවශ්‍ය වේ.

Custom Error Handling Middleware

මේ සඳහා අපි අපේම custom error handling middleware එකක් නිර්මාණය කරමු. මෙය අනෙකුත් middleware වලට වඩා වෙනස් වන්නේ, එයට arguments 4ක් (`err`, `req`, `res`, `next`) ලැබීමයි. මෙම middleware එක, `app.js` හෝ `server.js` file එකේ, අනෙකුත් සියලුම `app.use()` සහ routes වලට **පසුව** ඇතුළත් කළ යුතුය.

`middleware/errorHandler.js` file එක:


const errorHandler = (err, req, res, next) => {
    // Console එකේ error එක log කිරීම (debugging සඳහා)
    console.error(err.stack);

    // Client එකට යැවිය යුතු status code එක සහ message එක තීරණය කිරීම
    const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
    const message = err.message || 'Internal Server Error';

    // JSON format එකෙන් error response එක යැවීම
    res.status(statusCode).json({
        message: message,
        // Production mode එකේදී stack trace එක නොයැවීම වඩාත් සුදුසුය
        stack: process.env.NODE_ENV === 'production' ? null : err.stack,
    });
};

module.exports = errorHandler;
        

`server.js` file එකේ මෙය භාවිතා කිරීම:


const express = require('express');
const app = express();
const errorHandler = require('./middleware/errorHandler');

// ... (your routes and other middleware)

// Custom error handling middleware එක අවසානයටම යෙදීම
app.use(errorHandler);

app.listen(3000, () => console.log('Server started'));
        

Asynchronous Errors හසුරුවා ගැනීම

Database calls වැනි `async/await` භාවිතා කරන තැන් වලදී, errors හසුරුවා ගැනීමට `try...catch` blocks භාවිතා කිරීම ඉතා වැදගත් වේ. `catch` block එකේදී, අපි error එක `next()` function එකට pass කරමු. එවිට Express විසින් එය අපේ custom error handler middleware එකට යවනු ඇත.


router.post('/register', async (req, res, next) => {
    try {
        // ... (user registration logic)
        // යම් error එකක් ඇති විය හැකි code එක
        const user = await User.create({ ... });
        res.json(user);
    } catch (err) {
        // Error එක අපේ custom error handler එකට යැවීම
        next(err);
    }
});
        

💡 **Pro Tip:** `express-async-handler` වැනි package එකක් භාවිතා කිරීමෙන්, සෑම තැනකම `try...catch` ලිවීමෙන් වැළකී සිටිය හැක. එය ස්වයංක්‍රීයව async errors, error handler එකට pass කරයි.


2. Input Validation: පරිශීලකයා විශ්වාස නොකරන්න!

"Never Trust User Input"

මෙය security හි මූලික නීතියකි. Frontend එකෙන් එවන දත්ත (user input) හැමවිටම නිවැරදි, අපේක්ෂිත format එකෙන්, සහ ආරක්ෂාකාරී යැයි උපකල්පනය නොකළ යුතුය. දත්ත database එකේ save කිරීමට පෙර, backend එකේදී එම දත්ත වලංගු දැයි (validate) පරීක්ෂා කිරීම අනිවාර්ය වේ.

Validation මගින්:

3. Joi සමඟ Input Validation

**Joi** යනු JavaScript දත්ත validation කිරීම සඳහා ඇති ඉතා ජනප්‍රිය සහ බලවත් library එකකි. එය මගින් අපට දත්ත සඳහා පැහැදිලි නීති මාලාවක් (schema) නිර්වචනය කර, එම නීති වලට දත්ත ගැලපේදැයි පහසුවෙන් පරීක්ෂා කළ හැක.

Project Setup

  1. පළමුව `joi` package එක install කරගන්න:
  2. npm install joi
  3. අපි user registration route එක සඳහා validation schema එකක් නිර්මාණය කරමු.

Registration Route එක (Validation සමඟ):


const express = require('express');
const router = express.Router();
const Joi = require('joi');
// ... (other requires)

router.post('/register', async (req, res, next) => {
    
    // 1. Validation Schema එක නිර්වචනය කිරීම
    const registerSchema = Joi.object({
        email: Joi.string().email().required(),
        password: Joi.string().min(6).required(),
        name: Joi.string().optional()
    });

    try {
        // 2. req.body එක schema එකට අනුව validate කිරීම
        await registerSchema.validateAsync(req.body);

        // 3. Validation සාර්ථක නම්, ඉතිරි logic එක ක්‍රියාත්මක කිරීම
        const { email, password } = req.body;
        // ... (password hashing and user saving logic)

        res.status(201).send('User registered successfully');

    } catch (error) {
        // 4. Joi validation error එකක් නම්, එය 400 (Bad Request) ලෙස යැවීම
        if (error.isJoi) {
            return res.status(400).json({ message: error.details[0].message });
        }
        // වෙනත් error එකක් නම්, අපේ custom error handler එකට යැවීම
        next(error);
    }
});

module.exports = router;
        

දැන්, යම් කෙනෙක් email එකක් නොමැතිව, හෝ characters 6කට වඩා අඩු password එකක් සමඟ register වීමට උත්සාහ කළහොත්, Joi විසින් ස්වයංක්‍රීයව error එකක් generate කර, "password" must be at least 6 characters long" වැනි පැහැදිලි පණිවිඩයක් client එකට යවනු ඇත.


4. Logging 📝

Logging යනු application එකේ සිදුවන වැදගත් සිදුවීම්, විශේෂයෙන්ම errors, file එකකට හෝ logging service එකකට වාර්තා කර තැබීමයි. `console.log()` යනු development වලදී debugging සඳහා හොඳ වුවත්, production environment එකකදී, errors ස්ථිරවම වාර්තා කර තබා ගැනීම අත්‍යවශ්‍ය වේ.

මෙමගින්, යම් දෝෂයක් ඇති වූ විට, එයට හේතුව කුමක්ද, එය සිදු වූයේ කවදාද, කුමන request එකකදීද යන්න වැනි තොරතුරු නැවත පරීක්ෂා කර බලා, දෝෂ නිරාකරණය (debugging) කිරීමට විශාල උපකාරයක් ලැබේ.

Node.js සඳහා **Winston** සහ **Morgan** වැනි ඉතා ජනප්‍රිය logging libraries ඇත.

අපේ custom error handler එකේ, `console.error(err.stack)` වෙනුවට, Winston වැනි logger එකක් භාවිතා කර error එක file එකක log කිරීම production-ready application එකක් සඳහා හොඳම පුරුද්දයි.


Go Back to Module 7 Go to Module 9