🔍 Understanding Key Backend Concepts in a Real Express + MongoDB API

Jayesh VermaJayesh Verma
4 min read

When building backend APIs using Express.js and MongoDB, you’ll come across many concepts like route parameters, aggregation pipelines, and error handling. This article breaks down some essential backend topics using an example like getUserChannelProfile, a function used to fetch a YouTube-style user profile with subscriber data.

Let’s dive in 👇


🔧 Express.js Route Handler

In Express, a controller is a function that handles incoming requests. It's where we put the logic to process data and send a response.

jsCopyEditconst getUserChannelProfile = asyncHandler(async (req, res) => {
  // logic goes here
});
  • req (request): contains details about what the user sent (URL, params, body).

  • res (response): used to send back data or messages to the client.


🛣 Route Parameters in Express

When we access dynamic parts of a URL like /channel/:userName, we get the value using req.params.

jsCopyEditconst { userName } = req.params;

We also validate it to avoid errors:

jsCopyEditif (!userName?.trim()) {
  throw new ApiError(400, "Username is missing");
}

🔄 Async/Await and Error Handling

To handle database operations smoothly, we use async/await. It makes code easier to read and avoids “callback hell.”

We also use a package like express-async-handler to catch errors without repeating try...catch.

jsCopyEditconst getUserChannelProfile = asyncHandler(async (req, res) => { ... });

🧩 MongoDB Aggregation Pipeline

Aggregation in MongoDB lets us process data in stages, similar to a pipeline. Each stage transforms the data before passing it to the next.

Example:

jsCopyEditUser.aggregate([
  { $match: { userName: "jayesh" } },
  { $lookup: { ... } },
  { $addFields: { ... } },
  { $project: { ... } }
]);

🔍 $match in MongoDB

This stage filters documents, similar to a SQL WHERE clause.

jsCopyEdit{ $match: { userName: userName.toLowerCase() } }

We use .toLowerCase() to make the search case-insensitive.


🔗 $lookup in MongoDB

Used to join one collection with another, like SQL joins.

jsCopyEdit{
  $lookup: {
    from: "subscriptions",
    localField: "_id",
    foreignField: "channel",
    as: "subscribers"
  }
}
  • from: the other collection (subscriptions)

  • localField: the field from the current collection (_id)

  • foreignField: the matching field in the other collection

  • as: name for the resulting joined array


$addFields in MongoDB

Used to add new fields that didn’t exist before. Great for computed values.

jsCopyEdit{
  $addFields: {
    subscribersCount: { $size: "$subscribers" },
    isSubscribed: {
      $cond: {
        if: { $in: [req.user?._id, "$subscribers.subscriber"] },
        then: true,
        else: false
      }
    }
  }
}

🔢 $size in MongoDB

Returns the number of items in an array. Here, it's used to count followers.

jsCopyEditsubscribersCount: { $size: "$subscribers" }

🔎 $in in MongoDB

Checks if a specific value exists inside an array.

jsCopyEdit{ $in: [req.user?._id, "$subscribers.subscriber"] }

Used to check if the logged-in user is already a subscriber.


🔀 $cond in MongoDB

This is the if-else logic operator. It works like a ternary:

jsCopyEdit$cond: {
  if: <condition>,
  then: <value if true>,
  else: <value if false>
}

Example: Set isSubscribed to true or false based on user status.


🎯 $project in MongoDB

Specifies which fields to include in the final output:

jsCopyEdit{
  $project: {
    fullName: 1,
    userName: 1,
    avatar: 1,
    subscribersCount: 1,
    isSubscribed: 1
  }
}

1 means include this field, 0 means exclude.


🌐 HTTP Status Codes

APIs return status codes to tell the client what happened:

CodeMeaning
200OK – everything worked
400Bad Request – something is missing or wrong
404Not Found – user/channel doesn’t exist

📦 API Response Format

Instead of raw JSON, we use a consistent format using a custom class like ApiResponse.

jsCopyEditreturn res.status(200).json(
  new ApiResponse(200, channel[0], "User channel fetched successfully")
);

It includes:

  • Status code

  • Data

  • Message

This keeps frontend integration clean and predictable.


❌ Custom Error Handling (ApiError)

To handle errors in a clean way, we use a custom class:

jsCopyEditthrow new ApiError(404, "Channel does not exist");

This makes error messages more descriptive and easier to manage across the app.


🛡 Optional Chaining in JavaScript (?.)

This feature helps avoid crashes when accessing nested properties that might be undefined.

jsCopyEditreq.user?._id

Without optional chaining, this would throw an error if req.user is undefined.


✅ Conclusion

In just one backend API function, we covered a wide range of essential concepts:

  • Express route handling

  • MongoDB aggregation and operators

  • Error handling and response formatting

  • Optional chaining for safer code

Understanding these topics will help you write more reliable, efficient, and clean backend APIs. Keep practicing, and you’ll master these patterns in no time!

0
Subscribe to my newsletter

Read articles from Jayesh Verma directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jayesh Verma
Jayesh Verma