DevTinder Project - ref, populate and Thought process for writing API's
Code Demonstration Link
- DevTinder Backend Repository Project
Overview
Today's focus is on using ref, populate, and implementing a structured thought process for writing APIs. The work involves building an API to review and update the status of a connection request.
API Details
Endpoint:
- POST
/request/review/:status/:requestId
Purpose:
- To accept or reject a connection request by updating its status based on specific conditions.
Thought Process for API Development
Steps:
-
Getting the Logged-In User:
- Identify the user making the request using authentication mechanisms (e.g., JWT or session).
-
Retrieving Parameters:
- Extract
statusandrequestIdfrom the API parameters.
- Extract
-
Validating Input:
- Ensure that the
statusparameter only contains valid values:- Allowed values:
acceptedorrejected.
- Allowed values:
- Return an error response if the
statusis invalid.
- Ensure that the
-
Searching for the Connection Request:
- Query the database to find a connection request that:
- Matches the provided
requestId. - Has the
toUserIdfield equal to the logged-in user's ID. - Has the
statusset tointerested.
- Matches the provided
- Query the database to find a connection request that:
-
Updating the Status:
- If a matching connection request is found, update its
statusfield to the new value provided in the parameters. - Respond with a success message or the updated connection request data.
- If a matching connection request is found, update its
requestRouter.post(
"/request/review/:status/:requestId",
userAuth,
async (req, res) => {
try {
const loggedInUser = req.user;
const { status, requestId } = req.params;
//Validate Status
const allowedStatuses = ["accepted", "rejected"];
if (!allowedStatuses.includes(status)) {
return res.status(400).json({
message: "Invalid Status or Status not allowed",
success: false,
});
}
//validating the request
const connectionRequest = await ConnectionRequestModel.findOne({
_id: requestId,
toUserId: loggedInUser._id,
status: "intrested",
});
if (!connectionRequest) {
return res.status(404).json({
message: "request not found ",
success: false,
});
}
connectionRequest.status = status;
const data = await connectionRequest.save();
res.status(200).json({
message: "Connection request " + status,
data,
success: true,
});
} catch (error) {
res.status(400).send("ERROR:" + error.message);
}
}
);1. ref and populate in Mongoose
-
ref:- Defines relationships between collections in MongoDB.
- Used to reference documents from other collections (e.g., linking
toUserIdandfromUserIdto the User collection).
-
populate:- Populates referenced fields with actual document data instead of just IDs.
- Simplifies retrieving related data in a single query.
API Details (UserSide)
Endpoint:
- GET
/user/requests/received
Purpose:
- Fetch all pending connection requests where the logged-in user is the recipient (
toUserId) and the request status isinterested.
Thought Process for API Development
Steps:
-
Setting up the User Router:
- Create a dedicated router for user-related operations to improve modularity and organization.
-
Authenticating the API:
- Apply authentication middleware to ensure the user is logged in before processing the request.
- Use tokens (e.g., JWT) to validate and retrieve the logged-in user.
-
Fetching Connection Requests:
- Query the
ConnectionRequestcollection to retrieve all requests with:toUserIdmatching the logged-in user's ID.statusset tointerested.
- Query the
-
Building Relationships Between Schemas:
- Use
refin theConnectionRequestschema to reference theUserschema for thefromUserIdfield. - Populate the
fromUserIdfield to retrieve the related user's data.
- Use
-
Returning Specific Fields:
- Use
populateto fetch only thefirstNameandlastNameof thefromUserIduser. - Exclude unnecessary fields to optimize the response payload.
- Use
//Setting Reference
fromUserId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
required: true,
},
//Popuating Data
userRouter.get("/user/requests/recieved", userAuth, async (req, res) => {
try {
const loggedInUser = req.user;
const connectionRequests = await ConnectionRequestModel.find({
toUserId: loggedInUser._id,
status: "intrested",
}).populate("fromUserId", ["firstName", "lastName"]);
if (connectionRequests) {
return res.status(200).json({
connectionRequests,
});
}
} catch (error) {
res.status(400).send("ERROR:" + error.message);
}
});
```
---
## API Details
### Endpoint:
- **GET `/user/connections`**
### Purpose:
- Fetch all accepted connection requests where the logged-in user is either the sender (`fromUserId`) or the receiver (`toUserId`).
---
## Thought Process for API Development
### Steps:
1. **Fetching Connection Requests**:
- Query the `ConnectionRequest` collection to retrieve requests that:
- Have `status` set to `"accepted"`.
- Include the logged-in user’s ID in either the `toUserId` or `fromUserId` fields.
2. **Data Relationships**:
- Use relationships between the `ConnectionRequest` and `User` schemas to retrieve additional user data if necessary.
- Leverage Mongoose’s `populate` method to enhance the data with user details.
3. **Returning Results**:
- Filter and return all matching connection requests as part of the response.
- Optimize the response payload by including only relevant fields (e.g., user names, IDs).
userRouter.get("/user/connections", userAuth, async (req, res) => { try { const loggedInUser = req.user;
const connectionRequests = await ConnectionRequestModel.find({
$or: [
{ toUserId: loggedInUser._id, status: "accepted" },
{ fromUserId: loggedInUser._id, status: "accepted" },
],
}).populate("fromUserId", USER_SAFE_DATA);
const data = connectionRequests.map((row) => row.fromUserId);
res.status(200).json({
data,
});
} catch (error) { res.status(400).send("ERROR :" + error.message); } });
---