Basic Project & Handel Error ⇾ Explain Backend Series

Basic Project & Handel Error ⇾ Explain Backend Series

Error handling part -> 3

Create files and start Code :

গত পর্বে তুমি ফোল্ডার স্ট্রাকচার করেছিলা। এইপর্বে আমরা বেসিক লেবেল এর কিছু কাজ করব। এরর হ্যান্ডেল কনফিগারেশন সেটআপ এসব আরকি ।

তো শুরু করা যাক :

এর আগে সব কোড তুমি server.js ফাইল এ করেছিলা ।এখন আমরা শুরু করব app ফোল্ডার থেকে । app ফোল্ডার এ app.js নামে একটা ফাইল তৈরী করব ।তুমি express require করে app তৈরী করে ফেলো।

একটা health route বানাও ।যদিও হেলথ route এর তেমন কোন কাজ নেই ।তারপর route টা একটা ২০০ স্ট্যাটাস কোড জেনারেট করতে হবে । কারণ যখন তুমি api application কে সার্ভার এ ডেপ্লয় করবা , তখন ডেপ্লয় অটোমেশন সার্ভিস এই হেলথ route চেক করবে ।তখন এটা না দিলে কিছু এরর জেনারেট করবে

app.js ফাইল কোড:

  const express = require("express");

  const app = express();

  app.get("/health", (_req, res, _next) => {
    res.status(200).json({ message: "Success" });
  });

  module.exports  = app;

তুমি হয়ত খেয়াল করছ আমি req এবং next এই দুইটার আগে _ ব্যবহার করেছি ।এর কারণ হচ্ছে আমরা ভিতরে কোড ব্লক এ শুধু res ব্যবহার করেছি next এবং req ব্যবহার করিনি । যখন তুমি এপ্লিকেশন টি কোথাও ডেপ্লয় করতে যাবে, unused ভ্যারিয়েবল নামে একটা এরর ডেপ্লয়মেন্ট সার্ভিস গুলো জেনারেট করবে ।যখনি এর পেছনে _ আন্ডারস্কোর দিবা তখন আর এটা unused ভ্যারিয়েবল দেখাবে না ।আর যেহেতু আর্গুমেন্ট গুলো পসিশন আর্গুমেন্ট তাই বাদ দিয়ে দেয়াও সম্ভব হচ্ছে না ।

position আর্গুমেন্ট বলতে, তুমি যদি req আর্গুমেন্ট না নাও তাহলে res নিতে পারবা না ।একটার পরে আর একটা চলে আসে ।

এর পরে আমি app টাকে এক্সপোর্ট করছি module.exports =app লিখে এবং সার্ভার লিসেন করিনি।কারণ আমাদের এপ্লিকেশন টাকে পরবর্তীতে test করতে হবে ।এখান থেকে আমরা চাইলেই সার্ভার লিসেন পারতাম ।তাতে করে সার্ভার টা শুধু app.js ফাইলেই রান হতো।পুরো এপ্লিকেশন টাকে পেতাম না। আমাদের দরকার এপ্লিকেশন not only server।যেহেতু আমরা app টাকে এক্সপোর্ট করেদিছি এখন app একটা অবজেক্ট। তুমি চাইলে যে কোন জায়গা থেকে import করে লিসেন করতে পার ।

HTTP response status codes :

এই http স্টেটাস কোড গুলো আসলে indicate করে যে http রিকোয়েস্ট sucsessfully কমপ্লিট হয়েছে কিনা । তুমি যদি বিস্তারিত পড়তে চাও তবে এই link এ যাও ।

http স্টেটাস মূলত ৫টা ভাগে ভাগ করা হইসে :

  1. Informational responses (100–199)
  2. Successful responses (200–299)
  3. Redirection messages (300–399)
  4. Client error responses (400–499)
  5. Server error responses (500–599)

create server :

এখন তুমি তোমার server.js ফাইল এর সব মুছে ফেলে node js এর http মডিউল ইম্পোর্ট করে সার্ভার create করে ফেল।এবং সার্ভার রান করে দেখ ঠিক আছে কিনা ।

server.js code :

const http = require("http");

const app = require("./app/app");

const server = http.createServer(app);

server.listen(8000, () => {
  console.log("Server IS running");
});

ok খুব ভাল তুমি সার্ভার সেটআপ করে ফেলেছ ।কোন পোর্ট এ রান করেছ? পোর্ট 8000। এটা একটা স্ট্যাটিক পোর্ট হয়ে গেল ।যখন আমরা আমাদের এপ্লিকেশন টাকে সার্ভার এ ডেপ্লয় করব তখন সার্ভার তার ইচ্ছে মত পোর্ট ব্যবহার করবে।সেখানে যদি আমরা স্ট্যাটিক করে দেই তাহলে সেটা চেঞ্জ করা সম্ভব না। আর তাই পোর্ট হতে হবে ডাইনামিক । ডাইনামিক পোর্ট এর জন্য আমরা .env নামে প্যাকেজ এর সাহায্য নিব । এই .env হল ইনভর্নমেন্ট ভ্যারিয়েবল রাখার জায়গা। আমাদের এপ্লিকেশন এর যত সিক্রেট এবং কনফিডেন্সিয়াল ডাটা থাকবে সব .env ফাইল এ রাখতে হবে । তুমি process.env এর মাধ্যমে ব্যবহার করতে পার। Environment বা .env এর জন্য আমাদের কিছু npm প্যাকেজ এর দরকার হবে ।

তাই টার্মিনাল এ রান করছি এবং প্যাকেজ install করছি :

npm i dotenv

উপরের কম্যান্ড চালালে dotenv প্যাকেজ টা প্রজেক্ট এ ইনস্টল হয়ে যাবে।এরপর আমাদের দুটা ফাইল create করা দরকার হবে । প্রজেক্ট root / main ফোল্ডার এ .env , default.env ফাইল create কর । এই .env ফাইল টা গিট্ ignored করা থাকে ।তুমি চাইলে মেনুয়াললি করতে পার । আমি গিট্ ইগনোর ফাইল create করছি .gitignore main ফোল্ডার এ এবং toptal genaretor থেকে gitignore কোড জেনারেট করে নিয়ে .gitignore ফাইল এ পেস্ট করে রাখছি ।toptal থেকে ফাইল জেনারেট করতে ফার্স্ট দিতে হবে অপারেটিং সিস্টেম নাম (windows ),then editor নাম (visualstudio code ) এবং runtime নাম (node ) ।

এখানে দুইটা .env file বানানোর কারণ হচ্ছে আমরা .env তে রাখব সিক্রেট ডাটা এবং এই ফাইল গিট্ এ push করব না। আর default.env তে বলে দিব কি কি দরকার যেটা ছাড়া এপ্লিকেশন রান করবে না।টীম মেম্বার রা যেন দেখতে পারে ।তাই default.env ফাইল যাবে গিটহাব এ ।আশাকরি তুমি এখন বুজছ কেন লাগবে ।

আচ্ছা তাহলে এখন তুমি .env ফাইল এ যাও এবং PORT=4444 variable create করে ফেল ।এবার server.js এর মধ্যে PORT নামে ভ্যারিয়েবল create করে process.env.PORT || 8000 value সেট করে দাও।এর এক্সপ্লেইন করলে হয়, process.env.PORT থেকে ভেরিএবল নিয়ে এসো আর যদি না পাও তবে পোর্ট 8000 এ সার্ভার চালাও ।এবার তুমি যদি server.listen এর স্ট্যাটিক পোর্ট এর জায়গায় PORT ভ্যারিয়েবল পাস্ কর ,সেভ কর এবং সার্ভার রান কর দেখবা এখনও পোর্ট 8000 এই চলতেছে ?

কেন চলতেছে 8000 এ ?কারণ তুমি dotenv require বা ইম্পোর্ট কর নি ।ইম্পোর্ট আর কন্ফিগার করার নিয়ম হচ্ছে ।

require('dotenv').config()

server.js file code:

const http = require("http");

require("dotenv").config();

const app = require("./app/app");
//create server
const server = http.createServer(app);

//graph PORT on .env
const PORT = process.env.PORT || 8000;

// listening to server according to port 
server.listen(PORT, () => {
  console.log("Server IS running", PORT);
});

পারফেক্টলি সার্ভার 4444 পোর্ট এ রান করবে ।

Common Middleware Use :

তুমি কিছু কমন middleware গ্লোবালি require করে নিবা app.js ফাইল এ কারণ এগুলো সচর আচার লাগে ।সেক্ষেত্রে আমাদের দরকার cors , morgan এবং json।আপাতত এই গুলো হলেই চলবে ।

ইন্সটল morgan ,cors :

npm i cors
npm i morgan

app.js file code:

//require dotenv and give the path
require("dotenv").config("../.env");
const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const app = express();

// Initialization Global Middleware
app.use([morgan("dev"), cors(), express.json()]);

app.get("/health", (_req, res, _next) => {
  res.status(200).json({ message: "Success" });
});

module.exports = app;

code চেঞ্জ করার পরে অবশ্যই চেক করে নিবা সার্ভার চলছে কিনা ।কোন এরর হয়েছে কিনা ।

OUTPUT TERMINAL :

Screenshot from 2022-08-06 01-52-05.png

Global Error Handling

তুমি হেলথ route বানাইছ । route হিট করলে মেসেজ দিচ্ছে। thsts good, but এখন যদি তুমি এমন route এ হিট কর যেটা create করা নেই ?

ধর আমরা হোম route বানাইনি but হিট করলাম localhost:4444 তাহলে মেসেজ দিবে Cannot GET /।আমাদেরকে কোন পারফেক্ট মেসেজ দিচ্ছে না ।আবার ধরো যদি আমাদের সার্ভার ক্রাশ করে ?তাহলেও এরর দিবে ।তাই এই এরর গুলোকে হ্যান্ডেল করা দরকার ।

এবার একটা মেনুয়াল এরর throw করাই আমাদের কোড এ ।কোথায় করব ? হেলথ route এ ।

app.jscode :

app.get("/health", (_req, res, _next) => {
  //throw new error 
  throw new Error("error");
  res.status(200).json({ message: "Success" });
});
`

এখন যদি http://localhost:4444/health route এ গিয়ে হিট কর output আসবে :

Screenshot from 2022-08-06 02-22-50.png

এরকম মেসেজ আসবে । আমরা অবশ্যই আমাদের ক্লায়েন্ট কে এমন হাবিজাবি মেসেজ দেখাব না। একজন ডেভেলপার এর দায়িত্ব ক্লায়েন্ট কে সুন্দর করে মেসেজ দিয়া জানান এরর কি হয়েছে ।

তুমি মোটামুটি দুই ভাবে এরর টাকে প্রসেস করতে পার ।

  • প্রত্যেকটা জায়গায়, যেখানে এরর হবে ধরে ধরে প্রসেস করতে পার ।

  • অথবা একটা glober এরর হ্যান্ডেলার থাকবে ।যে কোন জায়গায় এরর হলেই গ্লোবাল এরর সেই মেসেজ জেনারেট করবে ।

তো আমরা আমাদের কোড এ global এরর হ্যান্ডেল করব ।

Handling 404 error by app.use :

app.js এর মধ্যে তুমি app.use ব্যবহার করে এরর middleware create করতে পার ।

// create middleware 404 error
app.use((_req, _res, next) => {
  const error = new Error("Resource Not Found");
  error.status = 404;
  next(error);
});

উপরের কোড এ আমরা error নামে new error অবজেক্ট create করছি যার মেসেজ হিসেবে Resource Not Found দিয়েছে ।এর পরে error স্ট্যাটাস দিয়া দিয়াছি 404। then নেক্সট কল করেছি কিন্তু আর্গুমেন্ট হিসেবে error দিয়ে দিয়েছি । next যদি ফাঁকা কল করাহয় তবে এটা success ধরে নেয় ।আর peramitar এ error পাস করে দিলে success হয় না।এই প্যার্টান টাকে বলাহয় error ফার্স্ট প্যাটার্ন ।

Handling Global error by app.use :

app.js এর মধ্যে তুমি again app.use ব্যবহার করে এরর middleware create করতে পার ।কিন্তু এবারের middleware টা একটু ভিন্ন । আমরা error নামে একটা এডিশনাল আর্গুমেন্ট নিব ।যে এরর আর্গুমেন্ট টা আমরা next ফাঙ্কশন এ পাস করে দিয়েছিলাম ।ফার্স্ট আর্গুমেন্ট এ সেটাকেই বসাব ।

  app.use((error, _req, res, _next) => {
    if (error.status) {
      return res.status(error.status).json({
        message: error.message,
      });
    }
    res.status(500).json({ message: "Something Went Wrong" });
  });

আমরা এখানে বললাম যদি error.status থাকে তাহলে return করবে এবং json আকারে রেসপন্স ব্যাক করবে এরর এ যে মেসেজ টা আছে সেটা ।আর error.status মানেই হচ্ছে সেটা আমাদের জেনারেট করা এরর ।

আর যদি সেটা না হয়, যদি স্টেটাস কোড ৫০০ বা তার বেশি হয় তাহলে সবসময় সেটা সার্ভার এরর ।তখন হাবিজাবি মেসেজ না দিয়ে জেসন আকারে মেসেজ দিবে message: "Something Went Wrong"।

app.js Final Code :

//require dotenv and give the path
require("dotenv").config("../.env");
const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const app = express();

// Initialization Global Middleware
app.use([morgan("dev"), cors(), express.json()]);

app.get("/health", (_req, res, _next) => {
  throw new Error("error");
  res.status(200).json({ message: "Success" });
});

app.use((_req, _res, next) => {
  const error = new Error("Resource Not Found");
  error.status = 404;
  next(error);
});

app.use((error, _req, res, _next) => {
  if (error.status) {
    return res.status(error.status).json({
      message: error.message,
    });
  }
  res.status(500).json({ message: "Something Went Wrong" });
});

module.exports = app;

এখন যদি আমরা http://localhost:4444/health route এ যাই তাহলে "message": "Something Went Wrong" দিবে কারণ আমাদের এরর throw করা আছে ।

যদি http://localhost:4444 route এ যাই যেটা create করা নাই তবে "message": "Resource Not Found" মেসেজ দিবে ।ক্লায়েন্ট কে কি মেসেজ দিব ,সেটা এখন আমাদের হাতে ।

আমরা এখন আমাদের throw করা এরর তুলে দিতে পারি। যেকোন এরর এখন মেসেজ দিয়েই শো করবে ।

Without throw error app.js file :

//require dotenv and give the path
require("dotenv").config("../.env");
const express = require("express");
const morgan = require("morgan");
const cors = require("cors");
const app = express();

// Initialization Global Middleware
app.use([morgan("dev"), cors(), express.json()]);

app.get("/health", (_req, res, _next) => {
  res.status(200).json({ message: "Success" });
});

app.use((_req, _res, next) => {
  const error = new Error("Resource Not Found");
  error.status = 404;
  next(error);
});

app.use((error, _req, res, _next) => {
  if (error.status) {
    return res.status(error.status).json({
      message: error.message,
    });
  }
  res.status(500).json({ message: "Something Went Wrong" });
});

module.exports = app;

Separate code By Structure :

তুমি যে এত এত কোড app.js ফাইল এ রেখেছ এতে করে app.js ভারী হয়ে যাচ্ছে । এই ফাইল এর responsobality হচ্ছে এপ্লিকেশন তাকে বুটকরা ।তাই যতটা সম্ভব ক্লিন রাখার চেষ্টা করতে হবে ।আমরা চাইলে সব এক ফাইল এ রেখেও কাজ করতে পারি but তাতে করে কোড মেনেজ করা কঠিন হয়ে যাবে ।

handel Middleware :

এখন middleware গুলোকে app folder এ middleware.js নামে ফাইল create করে সেখানে রাখব । এবং এক্সপোর্ট করে app.js ফাইল থেকে ইমপোর্ট করে app.use(middleware) use করব ।

middleware.js file code :

const express = require("express");
const morgan = require("morgan");
const cors = require("cors");

const middleware = [morgan("dev"), cors(), express.json()];

module.exports = middleware;

Handel Router :

আমরা route গুলোকে হ্যান্ডেল করার জন্য app ফোল্ডার এ routes.js নামে একটা ফাইল নিব ।এবং সেখানে এক্সপ্রেস এর Router নামক মডিউল ব্যবহার করে use করব । Router মূলত একটা ফাঙ্কশন । express.Router() কল করলে অরিজিনাল যে রাউটার আছে সেটা বেরহয়ে আসবে । router নামে ভেরিএবলে রেখে এক্সপোর্ট করে দিব ।এবং app.js থেকে হেলথ route কে বের করে router.js এ রাখব ।but app.get এর পরিবর্তে router.get হবে ।এবার app.js এ routes ইম্পোর্ট করে app.use(route) middleware use করব ।

routes.js file code :

const router = require("express").Router();

router.get("/health", (_req, res, _next) => {
  res.status(200).json({ message: "Success" });
});

module.exports = router;

Separet Error :

তুমি app folder এর মধ্যে error.js নামে একটা ফাইল নিবে সেখানে app.js ফাইল থেকে error ফাঙ্কশন গুলোকে নিয়ে আসবে then notFound ও globalErrorHandler variable এ রেখে অবজেক্ট আকারে এক্সপোর্ট করে দিবে ।app.js ফাইলে error require করে app.use(notFound); app.use(globalErrorHandler); use করবে ।

error.js file code :

const notFound = (_req, _res, next) => {
  const error = new Error("Resource Not Found");
  error.status = 404;
  next(error);
};

const globalErrorHandler = (error, _req, res, _next) => {
  if (error.status) {
    return res.status(error.status).json({
      message: error.message,
    });
  }
  res.status(500).json({ message: "Something Went Wrong" });
};

module.exports = {
  notFound,
  globalErrorHandler,
};

আমরা সব ফাইল গুলো app ফোল্ডার এ বানিয়েছি কারণ এগুলো সব app লেবেলের জন্য ।যখন আলাদা করে indivisual করবো তখন ব্যবহার করব separet ফোল্ডার গুলি ।

Final app.js file code

//require dotenv and give the path
require("dotenv").config("../.env");
const express = require("express");
//import error file
const { notFound, globalErrorHandler } = require("./error");
//import middleware file
const middleware = require("./middleware");
//import routes file
const { route } = require("./routes");

const app = express();

// Initialization Global Middleware
app.use(middleware);
app.use(route);
app.use(notFound);
app.use(globalErrorHandler);

module.exports = app;

আসা করি বুঝতে পেরেছ । আজকে এই পর্যন্তই পরের লেকচার এ আমরা একটা প্রজেক্ট করব । যেটা কনসেপ্ট গুলোকে আরো ক্লিয়ার করবে ।

Practice, hard work, honesty and team spirit are the ingredients of a correct attitude. Jame Wilson,