Full Stack Reddit Clone - Spring Boot, React, Electron App - Part 7
Introduction
Welcome to Part 7 of creating a Reddit clone using Spring Boot, and React.
What are we building in this part?
- Comment Request DTO
- Comment Response DTO
- Update Comment Repository
- Comment Service
- READ Comment Endpoint's
- CREATE Comment Endpoint
In Part 6 we added some custom exceptions, and made our post endpoint's!
Important Links
- Backend Source: https://github.com/MaxiCB/vox-nobis/tree/master/backend
- Frontend Source: https://github.com/MaxiCB/vox-nobis/tree/master/client
- Live URL: In Progress
Part 1: Comment DTO's 📨
Let's cover our the various DTO's we will need for comments. Inside com.your-name.backend.dto we will create the following classes.
- CommentRequest: Handles creation of the data that will be sent from the client to the API.
package com.maxicb.backend.dto;
import com.maxicb.backend.model.Post;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CommentRequest {
private Long id;
private String text;
private Post post;
}
- CommentResponse: Handles creation of the data that will be sent to the client from the API.
package com.maxicb.backend.dto;
import com.maxicb.backend.model.User;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.time.Instant;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class CommentResponse {
private Long id;
private String text;
private Long postId;
private Instant creationDate;
private User user;
}
Part 2: Update Comment Repository 📨
Let's cover updating our comment repository to support pagination, and sorting. Inside com.your-name.backend.repository we will Update the following classes.
- CommentRepository
package com.maxicb.backend.repository;
import com.maxicb.backend.model.Comment;
import com.maxicb.backend.model.Post;
import com.maxicb.backend.model.User;
import org.springframework.data.repository.PagingAndSortingRepository;
import java.util.List;
public interface CommentRepository extends PagingAndSortingRepository<Comment, Long> {
List<Comment> findByPost(Post post);
List<Comment> findAllByUser(User user);
}
Part 3: Comment Service 🌎
Let's cover the comment service our application will have. Inside com.your-name.backend.services add the following class.
- CommentService: Hold the logic for mapping data to and from DTO's, getting all comments's by postId, getting user specific comments's, and adding comments's to post's.
package com.maxicb.backend.service;
import com.github.marlonlom.utilities.timeago.TimeAgo;
import com.maxicb.backend.dto.CommentRequest;
import com.maxicb.backend.dto.CommentResponse;
import com.maxicb.backend.exception.PostNotFoundException;
import com.maxicb.backend.exception.UserNotFoundException;
import com.maxicb.backend.model.Comment;
import com.maxicb.backend.model.Post;
import com.maxicb.backend.model.User;
import com.maxicb.backend.repository.CommentRepository;
import com.maxicb.backend.repository.PostRepository;
import com.maxicb.backend.repository.UserRepository;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;
@Service
@AllArgsConstructor
@Transactional
public class CommentService {
private final UserRepository userRepository;
private final PostRepository postRepository;
private final CommentRepository commentRepository;
private final AuthService authService;
private CommentResponse mapToResponse(Comment comment) {
return CommentResponse.builder()
.id(comment.getId())
.text(comment.getText())
.postId(comment.getPost().getPostId())
.creationDate(TimeAgo.using(comment.getCreationDate().toEpochMilli()))
.userName(comment.getUser().getUsername())
.build();
}
private Comment mapToComment(CommentRequest commentRequest) {
User user = authService.getCurrentUser();
Post post = postRepository.findById(commentRequest.getPostId())
.orElseThrow(() -> new PostNotFoundException("Post not found with id: " + commentRequest.getPostId()));
return Comment.builder()
.text(commentRequest.getText())
.post(post)
.creationDate(Instant.now())
.user(user)
.build();
}
public CommentResponse save(CommentRequest commentRequest) {
return mapToResponse(commentRepository.save(mapToComment(commentRequest)));
}
public List<CommentResponse> getCommentsForPost(Long id) {
Post post = postRepository.findById(id)
.orElseThrow(() -> new PostNotFoundException("Post not found with id: " + id));
return commentRepository.findByPost(post)
.stream()
.map(this::mapToResponse)
.collect(Collectors.toList());
}
public List<CommentResponse> getCommentsForUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException("User not found with id: " + id));
return commentRepository.findAllByUser(user)
.stream()
.map(this::mapToResponse)
.collect(Collectors.toList());
}
}
Part 4: Comment Controller 🌐
Let's cover the comment controller our application will have. Inside com.your-name.backend.controller add the following class.
- CommentController: Hold the endpoints for fetching creating comments, fetching comments on a post, and specific user comments.
package com.maxicb.backend.controller;
import com.maxicb.backend.dto.CommentRequest;
import com.maxicb.backend.dto.CommentResponse;
import com.maxicb.backend.service.CommentService;
import lombok.AllArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/comments")
@AllArgsConstructor
public class CommentController {
private final CommentService commentService;
@PostMapping
public ResponseEntity<CommentResponse> addComment(@RequestBody CommentRequest commentRequest) {
return new ResponseEntity<>(commentService.save(commentRequest), HttpStatus.CREATED);
}
@GetMapping("/post/{id}")
public ResponseEntity<List<CommentResponse>> getCommentsByPost(@PathVariable Long id) {
return new ResponseEntity<>(commentService.getCommentsForPost(id), HttpStatus.OK);
}
@GetMapping("/user/{id}")
public ResponseEntity<List<CommentResponse>> getCommentsByUser(@PathVariable Long id) {
return new ResponseEntity<>(commentService.getCommentsForUser(id), HttpStatus.OK);
}
}
Conclusion 🔍
- To ensure everything is configured correctly you can run the application, and ensure there are no error in the console. Towards the bottom of the console you should see output similar to below
- If there are no error's in the console you can test the comment creation logic by sending a post request to http://localhost:8080/api/comments with the following data. You will still have to follow the same steps covered in the previous parts to login to an account to make post's, as well as create a subreddit, and valid post to add a comment to.
{
"postId": <post-id>,
"text": "My First Comment!!"
}
- In this article we added the CREATE && READ endpoints for creating and reading comments!.
Top comments (1)
Eagerly waiting for part 8. Please post