Skip to content

Conversation

@gocanto
Copy link
Collaborator

@gocanto gocanto commented Jul 28, 2025

Summary by CodeRabbit

  • New Features

    • Added a new API endpoint to list categories with pagination support.
    • Introduced structured response formats for categories, tags, posts, and users.
  • Refactor

    • Improved organization by moving request parsing, filtering, pagination, and response formatting logic to dedicated packages.
    • Unified and streamlined data transformation for API responses.
    • Changed the /posts route to accept POST requests instead of GET.
  • Bug Fixes

    • Enhanced pagination and counting logic for posts and categories to ensure accurate results.
  • Chores

    • Removed obsolete files and consolidated related functionality for better maintainability.
    • Updated pagination limits with specific caps for posts and categories.

@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

"""

Walkthrough

This change introduces a new categories API endpoint and refactors post-related handler logic for improved modularity. It adds repository, handler, and payload code for categories, centralizes pagination and payload transformation utilities, and removes redundant post handler files. The router and application boot sequence are updated to register the new categories route.

Changes

Cohort / File(s) Change Summary
Categories API Implementation
handler/categories.go, database/repository/categories.go, handler/payload/categories.go
Adds handler, repository method, and payload transformation for categories listing with pagination.
Router and App Bootstrapping
boost/router.go, boost/app.go
Adds Categories() route setup in router and registers it in app boot sequence.
Pagination Utilities
database/repository/pagination/support.go, handler/paginate/paginate.go, database/repository/pagination/pagination.go
Introduces generic count utility, pagination parameter parsing function, and splits max limit constants for posts and categories.
Posts Handler Refactor
handler/posts.go, database/repository/posts.go
Refactors post handler to use new payload and pagination packages; renames post repository method from GetPosts to GetAll.
Payload Structs and Transformers
handler/payload/posts.go, handler/payload/tags.go, handler/payload/users.go
Adds modular payload structs and transformer functions for posts, tags, categories, and users.
Legacy Post Handler Removal
handler/posts/response.go, handler/posts/transformer.go
Deletes old post response and transformer files, migrating logic to new payload modules.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • [feature] Get Posts/Post #59: Adds a "Posts" route and handler with database-backed logic, similar in structure and purpose to this PR's addition of a "Categories" route and handler.

Poem

Oh, what a leap for categories—
Now routes and payloads hop with ease!
Posts refactored, code made neat,
Pagination skips on nimble feet.
Payloads multiply, old files retire,
This bunny’s code is hopping higher!
🐇✨
"""

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/categories-endpoint

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary of Changes

Hello @gocanto, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request introduces a new API endpoint for categories, enabling clients to fetch paginated category data. Alongside this new feature, I've undertaken a significant refactoring effort to centralize and standardize pagination handling across the application's API handlers and to streamline the data structures used for API responses, ensuring a cleaner and more consistent public interface.

Highlights

  • New API Endpoint: I've added a new GET /categories endpoint to the application. This endpoint allows clients to retrieve a paginated list of categories, including their associated posts.
  • Pagination Logic Refactoring: I've extracted the common pagination parsing logic into a new, reusable handler/paginate package. This new utility is now used by both the new categories endpoint and the existing posts endpoint, promoting consistency and reducing duplication.
  • API Response Structure Refinement: I've refined the API response structures for User, Category, and Tag data within the handler/posts package. Specifically, I've renamed *Data structs to *Response and removed internal database timestamps (CreatedAt, UpdatedAt) from these public-facing response objects for a cleaner API contract.
  • Repository Method Renaming: For consistency, I've renamed the GetPosts method in the database/repository/posts.go to GetAll, aligning it with the newly introduced GetAll method in the categories repository.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in issue comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments or fill out our survey to provide feedback.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a new /categories endpoint with pagination. The review identifies a critical bug in the database query for fetching categories which would lead to incorrect pagination results. A code suggestion is provided to fix this. Additionally, a medium-severity suggestion is made to improve the project structure by relocating the now-shared response DTOs to a more appropriate package to enhance maintainability.

@gocanto gocanto changed the title add categories endpoint [feature] Add categories endpoint Jul 28, 2025
@gocanto gocanto marked this pull request as ready for review July 28, 2025 07:22
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (2)
database/repository/categories.go (2)

20-28: Critical: LIMIT clause may affect count calculation.

Based on past review feedback, applying LIMIT to the base query before counting (line 23, then count on line 26) can cause incorrect pagination metadata. The count operation may be limited to the page size rather than counting all matching records.

Verify that pagination.Count properly isolates the counting query from the LIMIT clause:

#!/bin/bash
# Description: Check if pagination.Count properly handles LIMIT isolation
# Expected: Count function should use a separate query session that ignores LIMIT

ast-grep --pattern 'func Count[$_]($_) {
  $$$
}'

35-35: Remove unnecessary Distinct() call.

As noted in past reviews, the Distinct() call is not needed since this query doesn't produce duplicate rows.

 	err := query.Preload("Posts").
 		Limit(paginate.Limit).
 		Offset(offset).
-		Distinct().
 		Find(&categories).Error
🧹 Nitpick comments (4)
database/repository/pagination/support.go (2)

5-5: Simplify the generic type constraint.

The generic type parameter T *int64 is unnecessary complexity. Since the function only works with *int64, use the concrete type directly.

-func Count[T *int64](numItems T, query *gorm.DB, session *gorm.Session, distinct string) error {
+func Count(numItems *int64, query *gorm.DB, session *gorm.Session, distinct string) error {

5-15: Add function documentation.

The function lacks documentation comments explaining its purpose, parameters, and return values. This is especially important for utility functions that will be used across multiple repositories.

+// Count executes a distinct count query on the provided GORM query and stores the result in numItems.
+// It clones the base query using the provided session and applies a distinct filter on the specified column.
+// Returns an error if the count operation fails, otherwise returns nil.
 func Count(numItems *int64, query *gorm.DB, session *gorm.Session, distinct string) error {
handler/payload/categories.go (1)

12-25: Optimize slice allocation for better performance.

The function uses append in a loop without pre-allocating the slice capacity. For better performance, especially with large datasets, consider pre-allocating the slice with the known length.

 func GetCategoriesResponse(categories []database.Category) []CategoryResponse {
-	var data []CategoryResponse
+	data := make([]CategoryResponse, 0, len(categories))

 	for _, category := range categories {
 		data = append(data, CategoryResponse{
 			UUID:        category.UUID,
 			Name:        category.Name,
 			Slug:        category.Slug,
 			Description: category.Description,
 		})
 	}

 	return data
 }
handler/payload/tags.go (1)

12-25: Optimize slice allocation for better performance.

The current implementation uses append in a loop which can cause multiple slice reallocations. Pre-allocate the slice with known capacity for better performance.

 func GetTagsResponse(tags []database.Tag) []TagResponse {
-	var data []TagResponse
+	data := make([]TagResponse, 0, len(tags))

 	for _, tag := range tags {
 		data = append(data, TagResponse{
 			UUID:        tag.UUID,
 			Name:        tag.Name,
 			Slug:        tag.Slug,
 			Description: tag.Description,
 		})
 	}

 	return data
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1437dba and a6bbffe.

📒 Files selected for processing (14)
  • boost/app.go (1 hunks)
  • boost/router.go (1 hunks)
  • database/repository/categories.go (2 hunks)
  • database/repository/pagination/support.go (1 hunks)
  • database/repository/posts.go (2 hunks)
  • handler/categories.go (1 hunks)
  • handler/paginate/paginate.go (1 hunks)
  • handler/payload/categories.go (1 hunks)
  • handler/payload/posts.go (1 hunks)
  • handler/payload/tags.go (1 hunks)
  • handler/payload/users.go (1 hunks)
  • handler/posts.go (5 hunks)
  • handler/posts/response.go (0 hunks)
  • handler/posts/transformer.go (0 hunks)
💤 Files with no reviewable changes (2)
  • handler/posts/response.go
  • handler/posts/transformer.go
🔇 Additional comments (20)
boost/router.go (1)

45-52: LGTM!

The Categories() method follows the established pattern used by other route handlers in this file. The implementation correctly creates the repository, handler, applies the middleware pipeline, and registers the route.

boost/app.go (1)

74-74: LGTM!

The addition of router.Categories() correctly integrates the new categories route into the application boot sequence. The placement after router.Posts() is logical and follows the established pattern.

handler/payload/categories.go (1)

5-10: LGTM!

The CategoryResponse struct is well-defined with appropriate JSON tags. The fields correctly map to the corresponding database.Category fields as shown in the relevant code snippet from app/database/schema.go.

handler/payload/users.go (1)

3-13: LGTM!

The UserResponse struct is well-defined with appropriate field types and JSON tags following snake_case naming conventions. The structure comprehensively covers user profile information needed for API responses.

handler/payload/tags.go (1)

5-10: LGTM! TagResponse struct correctly maps database fields.

The struct properly defines JSON serialization tags and maps the key fields from the database.Tag model.

handler/paginate/paginate.go (1)

9-37: LGTM! Solid pagination parameter parsing with proper validation.

The function correctly:

  • Handles missing or invalid query parameters gracefully
  • Applies sensible defaults using pagination constants
  • Validates bounds to prevent invalid page/limit values
  • Uses defensive programming practices

This is a good centralization of pagination logic that was previously scattered.

database/repository/posts.go (2)

18-18: Good method rename for consistency.

Renaming GetPosts to GetAll aligns with the new Categories.GetAll method, providing a consistent interface across repositories.


29-29: LGTM! Using centralized counting helper.

The switch to pagination.Count helper function centralizes the distinct counting logic and should provide consistent behavior across repositories.

handler/posts.go (2)

8-9: LGTM! Clean import organization for refactored packages.

The new imports properly separate concerns with dedicated paginate and payload packages.


29-29: Excellent refactoring to use modularized payload and pagination utilities.

The changes consistently update all references to use the new:

  • payload package for request/response transformations
  • paginate package for pagination parameter parsing
  • Repository method rename from GetPosts to GetAll

This improves code organization and reusability across handlers.

Also applies to: 37-39, 50-50, 63-63, 74-74

handler/categories.go (5)

3-13: LGTM! Clean import structure and dependency organization.

The imports are well-organized with proper aliasing for baseHttp to avoid naming conflicts with the custom HTTP package. The dependencies follow the established project structure.


15-23: LGTM! Proper dependency injection pattern.

The CategoriesHandler struct and constructor follow the established dependency injection pattern used throughout the codebase. The constructor function naming convention with Make prefix is consistent.


25-33: LGTM! Proper error handling and logging.

The method signature follows the established pattern with *http.ApiError return type. Error handling includes structured logging and appropriate HTTP error response.


35-45: LGTM! Consistent data transformation pattern.

The use of pagination.HydratePagination with a transformation function follows the established pattern. The inline transformation function correctly maps database fields to response payload fields, matching the CategoryResponse structure.


47-54: LGTM! Proper JSON encoding with error handling.

The JSON encoding includes proper error handling with structured logging and appropriate error response. The method returns nil on success, following the established pattern.

handler/payload/posts.go (5)

1-10: LGTM! Clean package structure and imports.

The package declaration and imports are well-organized. The use of baseHttp alias prevents naming conflicts with the custom HTTP utilities.


12-18: LGTM! Comprehensive filter structure.

The IndexRequestBody struct provides comprehensive filtering options for posts including title, author, category, tag, and text search. The JSON tags follow proper naming conventions.


20-35: LGTM! Complete post response structure.

The PostResponse struct correctly maps all fields from the database Post entity as confirmed by the schema in app/database/schema.go. The field mappings are accurate:

  • Basic fields (UUID, slug, title, etc.) match the database structure
  • Timestamp fields are properly typed
  • Associated entities (Categories, Tags, Author) are correctly represented
  • JSON tags follow consistent naming conventions

46-50: LGTM! Proper slug extraction and normalization.

The GetSlugFrom function correctly extracts the slug from the URL path parameter and applies proper normalization (trimming whitespace and converting to lowercase).


52-77: LGTM! Comprehensive post response transformation.

The GetPostsResponse function correctly transforms database Post entities to API responses:

  • All basic fields are properly mapped
  • Associated categories and tags are transformed using dedicated functions
  • Author information is correctly embedded as UserResponse
  • Field mappings match the database schema structure from app/database/schema.go

The transformation is comprehensive and maintains data integrity.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7dfc2f9 and cdd63ba.

📒 Files selected for processing (5)
  • database/repository/categories.go (2 hunks)
  • database/repository/pagination/pagination.go (1 hunks)
  • handler/categories.go (1 hunks)
  • handler/paginate/paginate.go (1 hunks)
  • handler/posts.go (5 hunks)
✅ Files skipped from review due to trivial changes (2)
  • database/repository/pagination/pagination.go
  • handler/categories.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • handler/paginate/paginate.go
  • handler/posts.go
🔇 Additional comments (3)
database/repository/categories.go (3)

7-7: LGTM!

The pagination package import is necessary for the new GetAll method functionality.


30-32: LGTM! Pagination counting logic is now correct.

The use of pagination.Count with the group parameter properly counts distinct categories before applying LIMIT, addressing the previous review concern about incorrect pagination metadata.


48-51: LGTM!

The pagination result construction is correct. Setting the number of items and using MakePagination follows the expected pattern for paginated responses.

@gocanto
Copy link
Collaborator Author

gocanto commented Jul 29, 2025

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Jul 29, 2025

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
handler/payload/posts.go (1)

37-45: LGTM! Past review feedback addressed.

The GetPostsFiltersFrom function now correctly maps all fields including the Text field that was previously missing. All request filters are properly transformed to query filters.

🧹 Nitpick comments (2)
handler/payload/categories.go (1)

12-25: Consider pre-allocating slice capacity for better performance.

The transformation logic is correct, but you can improve performance by pre-allocating the slice capacity to avoid multiple reallocations during append operations.

 func GetCategoriesResponse(categories []database.Category) []CategoryResponse {
-	var data []CategoryResponse
+	data := make([]CategoryResponse, 0, len(categories))
 
 	for _, category := range categories {
 		data = append(data, CategoryResponse{
 			UUID:        category.UUID,
 			Name:        category.Name,
 			Slug:        category.Slug,
 			Description: category.Description,
 		})
 	}
 
 	return data
 }
handler/payload/tags.go (1)

12-25: Consider pre-allocating slice capacity for better performance.

The transformation logic is correct, but the slice could be pre-allocated with known capacity to avoid potential reallocations during append operations.

-func GetTagsResponse(tags []database.Tag) []TagResponse {
-	var data []TagResponse
+func GetTagsResponse(tags []database.Tag) []TagResponse {
+	data := make([]TagResponse, 0, len(tags))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1437dba and aaf3c22.

📒 Files selected for processing (15)
  • boost/app.go (1 hunks)
  • boost/router.go (1 hunks)
  • database/repository/categories.go (2 hunks)
  • database/repository/pagination/pagination.go (1 hunks)
  • database/repository/pagination/support.go (1 hunks)
  • database/repository/posts.go (2 hunks)
  • handler/categories.go (1 hunks)
  • handler/paginate/paginate.go (1 hunks)
  • handler/payload/categories.go (1 hunks)
  • handler/payload/posts.go (1 hunks)
  • handler/payload/tags.go (1 hunks)
  • handler/payload/users.go (1 hunks)
  • handler/posts.go (5 hunks)
  • handler/posts/response.go (0 hunks)
  • handler/posts/transformer.go (0 hunks)
💤 Files with no reviewable changes (2)
  • handler/posts/response.go
  • handler/posts/transformer.go
🔇 Additional comments (20)
database/repository/pagination/support.go (1)

5-15: LGTM! Well-designed generic pagination helper.

The Count function provides a clean, reusable solution for counting distinct records with proper query cloning and error handling. The generic constraint T *int64 ensures type safety while maintaining flexibility.

database/repository/pagination/pagination.go (1)

6-7: Good refactoring to support entity-specific pagination limits.

Separating the pagination limits allows for more granular control over API performance. The smaller limit for categories (5) compared to posts (10) makes sense given different usage patterns.

boost/app.go (1)

74-74: LGTM! Proper integration of categories route.

The addition of router.Categories() follows the established pattern and correctly integrates the new endpoint into the application boot sequence.

boost/router.go (2)

45-52: LGTM! Categories endpoint follows established patterns.

The new Categories() method correctly follows the established routing pattern with proper repository initialization, handler creation, and middleware wrapping.


41-41: Confirm breaking change for /posts endpoint

The update to

r.Mux.HandleFunc("POST /posts", index)

replaces the previous GET listing route and is a breaking change that deviates from REST conventions. I did not find any references to the old GET /posts route in API docs (MD/TXT/JSON) within this repo.

Please verify:

  • All existing clients, SDKs, and API documentation have been updated to use POST /posts.
  • Consider deprecating the old GET /posts endpoint (or providing both) and clearly communicating the change to consumers.

The new GET /categories route follows the established pattern and looks good.

handler/payload/users.go (1)

3-13: LGTM! Clean and well-structured user response payload.

The UserResponse struct follows Go naming conventions, uses consistent snake_case JSON tags, and includes appropriate fields for user profile data. The structure is clean and ready for API responses.

handler/payload/tags.go (1)

5-10: LGTM! Well-structured tag response payload.

The TagResponse struct follows consistent naming conventions and includes appropriate fields for tag representation.

database/repository/posts.go (2)

29-31: No changes needed for pagination.Count generic parameter
The Count function is declared as func Count[T *int64](…), so specializing it as Count[*int64](&numItems, …) is exactly correct.


18-18: Generic type parameter usage and method rename are correct

  • The GetPostsGetAll rename is applied consistently; no remaining calls to GetPosts were found.
  • The call pagination.Count[*int64](&numItems, …) matches the definition
    func Count[T *int64](numItems T, …) error&numItems is indeed a *int64, so the explicit type argument is valid.

The original concern about the generic type parameter was unfounded.

Likely an incorrect or invalid review comment.

handler/posts.go (4)

8-9: LGTM! Clean import updates for the refactoring.

The new imports align perfectly with the package restructuring, moving from the old posts-specific package to the more generic payload and paginate packages.


29-29: Excellent refactoring to use the new payload and pagination utilities.

The migration from posts.IndexRequestBody to payload.IndexRequestBody and the use of paginate.MakeFrom with the new filter extraction logic shows good separation of concerns.

Also applies to: 38-39


37-37: Perfect alignment with repository method rename and response transformation.

The updated calls to GetAll (renamed from GetPosts) and payload.GetPostsResponse maintain consistency with the broader refactoring effort.

Also applies to: 50-50


63-63: Consistent use of new payload utilities in Show method.

The migration to payload.GetSlugFrom and payload.GetPostsResponse maintains consistency with the Index method refactoring.

Also applies to: 74-74

database/repository/categories.go (1)

16-52: LGTM! Past review feedback has been properly addressed.

The GetAll method implementation correctly addresses the previous review concerns:

  1. Pagination counting fixed: Uses pagination.Count helper to count distinct categories before applying LIMIT/OFFSET, ensuring accurate pagination metadata.

  2. Preload filtering implemented: The Preload("Posts", ...) now correctly filters to only published, non-deleted posts instead of loading all posts.

  3. Proper grouping: Uses GROUP BY categories.id, categories.slug to handle potential duplicates from JOINs.

The query logic is sound - using INNER JOINs to only return categories with published posts appears intentional for an API endpoint that displays content.

handler/categories.go (2)

15-23: LGTM! Clean handler structure and constructor.

The CategoriesHandler struct and MakeCategoriesHandler constructor follow good Go patterns with clear dependency injection of the repository.


25-54: Well-implemented HTTP handler with proper error handling.

The Index method demonstrates good practices:

  1. Proper pagination: Uses paginate.MakeFrom with sensible default page size of 5
  2. Error handling: Logs errors and returns appropriate HTTP responses
  3. Response transformation: Uses pagination hydration to convert domain models to API responses
  4. JSON encoding safety: Handles encoding errors gracefully

The inline transformation function on lines 37-44 keeps the response mapping close to usage, which is appropriate for this simple case.

handler/payload/posts.go (4)

12-18: LGTM! Complete request structure for post filtering.

The IndexRequestBody struct includes all necessary fields for comprehensive post filtering including title, author, category, tag, and text search.


20-35: Well-structured response payload with proper associations.

The PostResponse struct provides a comprehensive API response format with:

  • All essential post metadata fields
  • Proper timestamp handling with *time.Time for nullable PublishedAt
  • Clean separation of associations (categories, tags, author)

47-51: LGTM! Safe path parameter extraction.

The GetSlugFrom function safely extracts and normalizes the slug parameter with proper trimming and case normalization.


53-78: Comprehensive post response transformation.

The GetPostsResponse function provides complete mapping from database entity to API response, properly handling all fields and nested associations. The inline author mapping ensures all user fields are included.

@gocanto gocanto merged commit 6ec7524 into main Jul 29, 2025
4 checks passed
@gocanto gocanto deleted the feature/categories-endpoint branch July 29, 2025 08:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants