Introduction: Why Handling Friend Requests Matters

Welcome back! In the previous lesson, you learned how to let users send friend requests in your reading tracker app. Now, it’s time to manage those requests. Handling friend requests is a key part of any social feature. It lets users see who wants to connect with them and decide whether to accept or decline those requests.

In this lesson, you will learn how to list incoming friend requests, accept or decline them, and make sure only the right user can take action. By the end, you’ll be able to build endpoints that keep your app’s friendships secure and up to date.

Viewing Incoming Friend Requests

Let’s start by allowing users to see their incoming friend requests. Here we will implement a read endpoint that returns all pending requests where the authenticated user is the recipient. In our service file, we add a method that filters pending requests; in our controller, we expose GET /friends/requests and derive the recipient from the JWT via @CurrentUser().

  • Filters the in-memory friendRequests store to pending requests for the given userId.
  • Uses DatabaseService.getFriendRequests() introduced previously.
  • Keeps business logic in the service; no token parsing here (controller handles identity).

In this addition, we keep the read logic simple and easily testable: a pure filter over the in-memory collection. This focuses the controller on extracting the acting user while the service owns query semantics. Because it returns only pending requests, the UI/clients can reliably present actionable items. This foundation also makes it easy to extend filtering (e.g., pagination) later if needed.

Accepting or Declining Friend Requests

Now, let’s allow users to accept or decline these requests. Here we will implement state transitions for requests: recipients can accept or decline exactly once. In our DTO file, we validate the requested status; in our service, we enforce existence, pending-only handling, and recipient-only authorization; in our controller, we expose PATCH /friends/requests/:requestId.

  • Validates that status is one of 'accepted' | 'declined'.
  • Prevents invalid transitions early at the request boundary.
  • Keeps the controller/service simpler by guaranteeing input shape.

The DTO centralizes input validation, ensuring only valid transitions hit the service layer. By rejecting invalid values early, we keep the business logic focused and reduce branching. The pattern mirrors typical NestJS flows where DTOs act as the first line of defense against bad inputs.

Now let's define a service method which encodes the core invariants for request lifecycle management. By checking existence, pending status, and recipient identity, we prevent unauthorized or duplicate actions. The mutual friendIds update ensures the friendship graph is consistent on both sides. If declined, we persist the decision while leaving friendship data unchanged.

Testing and Error Handling with cURL

Let’s see how this works in practice using cURL commands. These examples show both correct and incorrect usage.

1. List incoming requests as Bob:

Output:

2. Accept the request as Bob:

3. Try to decline as Alice (not the recipient):

Output:

4. Try to accept the same request again (double-handling):

Output:

These examples show how the system enforces the rules and keeps your data safe.

Summary And Practice Preview

In this lesson, you learned how to let users view, accept, or decline incoming friend requests. You saw how to secure these actions so only the right user can handle each request, and how to prevent requests from being handled more than once. You also practiced using cURL to test both successful and failed scenarios.

Next, you’ll get hands-on practice with these endpoints. You’ll also see how confirmed friendships are stored, which will prepare you for the next unit — listing a user’s friends. Keep up the good work!

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal