List Posts Endpoint

Building on our tag management implementation from previous lessons, we'll now create a flexible endpoint for retrieving blog posts.

This endpoint will support filtering by categories and tags while ensuring only published content is accessible to users.

The feature enables content discovery and forms the foundation for our blog's public interface.

Data Transfer Object Creation

The PostDto encapsulates all necessary post information for API responses:

@Data @Builder @NoArgsConstructor @AllArgsConstructor public class PostDto { private UUID id; private String title; private String content; private AuthorDto author; private CategoryDto category; private Set<TagDto> tags; private Integer readingTime; private LocalDateTime createdAt; private LocalDateTime updatedAt; private PostStatus status; }

Warning

I use postStatus as the instance variable for this class, which causes some troubleshooting later! To avoid this use status.

Repository Implementation

The PostRepository provides flexible query methods for post retrieval:

@Repository public interface PostRepository extends JpaRepository<Post, UUID> { List<Post> findAllByStatusAndCategoryAndTagsContaining(PostStatus status, Category category, Tag tag); List<Post> findAllByStatusAndCategory(PostStatus status, Category category); List<Post> findAllByStatusAndTagsContaining(PostStatus status, Tag tag); List<Post> findAllByStatus(PostStatus status); }

Service Layer Definition

The PostService interface defines our core post retrieval functionality:

public interface PostService { List<Post> getAllPosts(UUID categoryId, UUID tagId); }

Service Implementation

The PostServiceImpl orchestrates post retrieval with proper filtering:

@Service @RequiredArgsConstructor public class PostServiceImpl implements PostService { private final PostRepository postRepository; private final CategoryService categoryService; private final TagService tagService; @Override @Transactional(readOnly = true) public List<Post> getAllPosts(UUID categoryId, UUID tagId) { if(categoryId != null && tagId != null) { Category category = categoryService.getCategoryById(categoryId); Tag tag = tagService.getTagById(tagId); return postRepository.findAllByStatusAndCategoryAndTagsContaining( PostStatus.PUBLISHED, category, tag ); } if(categoryId != null) { Category category = categoryService.getCategoryById(categoryId); return postRepository.findAllByStatusAndCategory( PostStatus.PUBLISHED, category ); } if(tagId != null) { Tag tag = tagService.getTagById(tagId); return postRepository.findAllByStatusAndTagsContaining( PostStatus.PUBLISHED, tag ); } return postRepository.findAllByStatus(PostStatus.PUBLISHED); } }

Mapper Configuration

The PostMapper interface handles entity-to-DTO conversion:

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.IGNORE) public interface PostMapper { @Mapping(target = "author", source = "author") @Mapping(target = "category", source = "category") @Mapping(target = "tags", source = "tags") PostDto toDto(Post post); }

Post Controller Setup

The PostController serves as the entry point for all post-related operations:

@RestController @RequestMapping(path = "/api/v1/posts") @RequiredArgsConstructor public class PostController { private final PostService postService; private final PostMapper postMapper; private final UserService userService; @GetMapping public ResponseEntity<List<PostDto>> getAllPosts( @RequestParam(required = false) UUID categoryId, @RequestParam(required = false) UUID tagId) { List<Post> posts = postService.getAllPosts(categoryId, tagId); List<PostDto> postDtos = posts.stream().map(postMapper::toDto).toList(); return ResponseEntity.ok(postDtos); } }

Exception Handling

Let's also add a block to handle EntityNotFoundException in the ErrorController:

@RestController @ControllerAdvice @Slf4j public class ErrorController { // ... @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity<ApiErrorResponse> handleEntityNotFoundException(EntityNotFoundException ex) { ApiErrorResponse error = ApiErrorResponse.builder() .status(HttpStatus.NOT_FOUND.value()) .message(ex.getMessage()) .build(); return new ResponseEntity<>(error, HttpStatus.NOT_FOUND); } }

Summary

  • Created flexible post retrieval endpoint with optional category and tag filtering
  • Implemented comprehensive DTO structure for post representation
  • Added repository methods for efficient post querying
  • Established service layer with proper transaction management
  • Configured MapStruct mapper for clean entity-to-DTO conversion
© 2026 Devtiro Ltd. All rights reserved