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