Conversation
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> - Rearranged the architecture into layered architecture, focusing on articles (making minimal changes in other features just to support the articles feature) - Fixed API summaries to match https://realworld-docs.netlify.app/specifications/backend/endpoints For context see [the added ARCHITECTURE.md file](https://github.com/agnyz/bedstack/blob/3bae8b09298d43d0019bbf1609287790d25c82a5/ARCHITECTURE.md). Continued from #153 --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a comprehensive, RESTful articles and comments API with full CRUD operations, authentication, and validation. - Added support for article feeds, favoriting, and following authors. - Enhanced error handling with more descriptive error responses. - Implemented new endpoints and DTOs for articles, comments, and user profiles. - **Bug Fixes** - Improved type safety and consistency across data models and API responses. - **Documentation** - Added detailed architecture and project structure documentation. - **Refactor** - Restructured codebase for better modularity, type safety, and maintainability. - Replaced and reorganized schema and interface definitions for articles and comments. - **Chores** - Updated configuration files for linting, formatting, and tooling. - Enhanced TypeScript configuration and path aliases. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
|
Warning Rate limit exceeded@yamcodes has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 22 minutes and 2 seconds before requesting another review. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. 📒 Files selected for processing (5)
WalkthroughThis change refactors the codebase into a modular, layered architecture for articles, comments, profiles, and tags, introducing controllers, services, repositories, schemas, DTOs, interfaces, and mappers for each feature. It replaces plugin-based routing with controller-based routing, standardizes error handling, enhances type safety, and adds extensive documentation and utility modules. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller
participant Service
participant Repository
participant Database
Client->>Controller: HTTP Request (e.g., POST /articles)
Controller->>Service: Validate & pass request data
Service->>Repository: Transform & enforce business logic
Repository->>Database: Query/Mutate data
Database-->>Repository: Return DB result
Repository-->>Service: Return data/rows
Service-->>Controller: Map to domain/DTO, handle errors
Controller-->>Client: HTTP Response (DTO or error)
Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> - Refactor Comments into our Layered architecture by creating dedicated mappers, dto, interface, and controller just like article - separate mappers into atomic files --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced a dedicated comments API with endpoints to create, retrieve, and delete comments on articles. - Added structured response formats for single and multiple comments. - **Refactor** - Separated comment-related routes from the articles API to a standalone comments controller. - Improved data mapping for articles and comments using dedicated mapper functions and centralized exports. - **Bug Fixes** - Corrected import paths for comments schema to ensure consistency. - **Chores** - Enhanced type safety and maintainability with new and updated interfaces for comments. - Updated documentation and response schemas for improved API clarity. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> Refactor Tags to Layered Architecture. --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced controllers for articles, comments, and tags, enabling RESTful endpoints for managing articles, comments, and tags. - Added comprehensive DTOs and mappers for structured request/response validation and data transformation. - Implemented modular architecture with clear separation of concerns for controllers, services, repositories, and mappers. - Added detailed error handling with standardized error responses. - **Improvements** - Refactored the codebase to enhance type safety, modularity, and maintainability. - Updated service and repository methods for better consistency and clarity. - Improved project structure and documentation for easier navigation and development. - **Documentation** - Added detailed architecture and project structure documentation to guide contributors and maintainers. - **Chores** - Added and updated configuration files for linting, formatting, and project tooling. - Updated TypeScript configuration and path aliases for improved developer experience. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> Refactor of Users to Layered Architecture --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced new user registration, login, profile retrieval, and profile update endpoints following the RealWorld API specification. - Added comprehensive data validation and response formatting for user-related operations. - Implemented new data structures and type definitions for user and tag entities to improve consistency and type safety. - **Refactor** - Replaced the previous user plugin with a new controller-based approach for handling user routes. - Updated internal mappings and service logic for better separation of concerns and maintainability. - **Chores** - Consolidated and reorganized type definitions, data transfer objects, and mappers for easier import and improved code structure. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> Refactor Profiles into Layered Architecture Closes #167 --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved consistency and structure of profile data returned by the API, including clearer typing for profile responses. - **Bug Fixes** - Corrected a typo in the internal variable name for the profiles repository. - **Refactor** - Unified naming conventions and response handling for profile endpoints. - Centralized and standardized profile data transformation and mapping. - Enhanced route descriptions and response schemas for profile-related endpoints. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
### Description <!-- Provide a comprehensive description here about what your PR aims to solve. --> <!-- You may also add additional context --> Misc refactor to Layered Architecture Closes #168 --- ### PR Checklist <!-- Please do not remove this section --> <!-- Mark each item with an "x" ([ ] becomes [x]) --> - [x] Read the Developer's Guide in [CONTRIBUTING.md](https://github.com/agnyz/bedstack/blob/main/CONTRIBUTING.md) - [x] Use a concise title to represent the changes introduced in this PR - [x] Provide a detailed description of the changes introduced in this PR, and, if necessary, some screenshots - [x] Reference an issue or discussion where the feature or changes have been previously discussed - [x] Add a failing test that passes with the changes introduced in this PR, or explain why it's not feasible - [x] Add documentation for the feature or changes introduced in this PR to the docs; you can run them with `bun docs` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Introduced standardized error handling with a new `RealWorldError` class and structured error responses. - Added utility modules for error formatting, date operations, and validation tasks. - Added reusable interface types for API responses, pagination, and common entities. - Centralized and expanded constants for pagination and error messages. - **Refactor** - Unified and simplified error handling across services, replacing custom error classes with standardized errors and HTTP status codes. - Updated import paths throughout the codebase for consistency using a single alias pattern. - Reorganized and clarified project structure documentation and code organization. - Improved and centralized constants and utility exports for easier access. - **Bug Fixes** - Fixed broken Markdown links and standardized code block formatting in documentation. - **Chores** - Simplified TypeScript path aliases in configuration. - Cleaned up whitespace and improved formatting in all documentation files. - **Documentation** - Expanded and clarified project structure, usage guidelines, and added new documentation for utilities and interfaces. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…s-env and drizzle-seed, and remove @faker-js/faker. Adjust undici-types version and add new dependencies including isexe, path-key, and shebang-command.
…oving comments and users imports to align with the layered architecture.
…iminating the ignore files section.
There was a problem hiding this comment.
Actionable comments posted: 11
♻️ Duplicate comments (1)
src/articles/articles.service.ts (1)
143-155: Apply same database constraint solution for update race conditionThis has the same race condition issue as the create method. The slug uniqueness should be enforced at the database level.
🧹 Nitpick comments (23)
CONTRIBUTING.md (1)
30-30: Trim trailing whitespace
Removed an extra space at the end of the Biome description. Consider hyphenating “Rust-based” for correctness.src/comments/mappers/to-new-comment-row.mapper.ts (1)
3-13: Simplify parameter structure for better API design.The first parameter
body: { body: string }creates unnecessary nesting. Consider flattening the parameters or using destructuring for a cleaner interface.-export function toNewCommentRow( - body: { body: string }, - articleId: number, - authorId: number, -): NewCommentRow { - return { - body: body.body, - articleId, - authorId, - }; -} +export function toNewCommentRow( + body: string, + articleId: number, + authorId: number, +): NewCommentRow { + return { + body, + articleId, + authorId, + }; +}Alternatively, if you need to maintain the object structure for consistency with other parts of the codebase, consider using destructuring:
-export function toNewCommentRow( - body: { body: string }, - articleId: number, - authorId: number, -): NewCommentRow { - return { - body: body.body, - articleId, - authorId, - }; -} +export function toNewCommentRow( + { body }: { body: string }, + articleId: number, + authorId: number, +): NewCommentRow { + return { + body, + articleId, + authorId, + }; +}src/articles/mappers/to-create-article-input.mapper.ts (1)
4-13: Clean and correct mapper implementation.The
toCreateArticleInputfunction correctly transforms the DTO to the input interface, properly handling the optionaltagListfield with nullish coalescing. The implementation is type-safe and follows good functional programming practices.Consider adding JSDoc documentation for consistency with other mappers:
+/** + * Map a CreateArticleDto to CreateArticleInput + * @param dto - The DTO containing article data + * @returns The mapped input object + */ export function toCreateArticleInput({ article, }: CreateArticleDto): CreateArticleInput {src/articles/mappers/to-new-article-row.mapper.ts (1)
4-13: Clean mapper implementation with proper separation of concerns.The function correctly transforms input data and generates a slug from the title. However, consider that slug uniqueness isn't guaranteed - multiple articles with the same title would generate identical slugs.
Consider handling potential slug collisions at the service layer:
export function toNewArticleRow( input: CreateArticleInput, authorId: number, ): NewArticleRow { return { ...input, slug: slugify(input.title), authorId, }; }If slug uniqueness is required, you might want to add a timestamp or random suffix generation at the service level when conflicts are detected.
src/comments/mappers/to-comment-response.mapper.ts (1)
4-17: Well-structured mapper with proper date handling.The mapper correctly transforms domain objects to response DTOs and handles date conversion to ISO strings. Consider adding null checks for optional author fields if they can be undefined.
If
bioorimagecan be null/undefined, consider defensive mapping:export const toCommentResponse = (comment: IComment): CommentResponseDto => ({ comment: { id: comment.id, createdAt: comment.createdAt.toISOString(), updatedAt: comment.updatedAt.toISOString(), body: comment.body, author: { username: comment.author.username, - bio: comment.author.bio, - image: comment.author.image, + bio: comment.author.bio ?? null, + image: comment.author.image ?? null, following: comment.author.following, }, }, });src/common/errors/realworld.error.ts (1)
4-12: Well-designed error class following RealWorld specification.The error class properly extends Error and provides structured error handling. The JSON.stringify approach for the message works well for logging and debugging.
Consider adding a toString method for more readable error messages in logs:
export class RealWorldError extends Error { constructor( public status: number, public errors: Record<string, string[]>, ) { super(JSON.stringify(errors)); this.name = 'RealWorldError'; } + + toString(): string { + return `${this.name} (${this.status}): ${JSON.stringify(this.errors)}`; + } }src/articles/dto/create-article.dto.ts (1)
3-11: Comprehensive DTO with proper validation rules.The DTO correctly validates required fields with minimum length constraints and handles optional tagList. The validation rules align well with typical article creation requirements.
Consider adding maximum length validation to prevent extremely long inputs:
export const CreateArticleDto = t.Object({ article: t.Object({ - title: t.String({ minLength: 1 }), - description: t.String({ minLength: 1 }), - body: t.String({ minLength: 1 }), + title: t.String({ minLength: 1, maxLength: 255 }), + description: t.String({ minLength: 1, maxLength: 500 }), + body: t.String({ minLength: 1, maxLength: 50000 }), tagList: t.Optional(t.Array(t.String({ minLength: 1 }))), }), });src/articles/interfaces/article.interface.ts (1)
14-21: Address the TODO comment for proper type definition.The current approach of using
Omitand intersection types for the author field is a valid workaround, but the TODO comment indicates this should be properly addressed. Consider creating a dedicatedIArticleAuthorinterface in the profiles module to avoid this hack.Would you like me to help create a proper
IArticleAuthorinterface to replace this temporary solution?drizzle/reset.ts (2)
8-30: Consider adding environment protection for the reset script.The SQL logic is correct and properly handles cascading deletions with
quote_identfor injection protection. However, consider adding environment checks to prevent accidental execution in production.async function reset() { + // Prevent accidental execution in production + if (process.env.NODE_ENV === 'production') { + console.error('❌ Reset script cannot be run in production environment'); + process.exit(1); + } + console.log('⏳ Resetting database...');
37-37: Consider making process exit conditional.The unconditional
process.exit(0)might not be ideal if this function needs to be imported and used programmatically elsewhere.- process.exit(0); + if (require.main === module) { + process.exit(0); + }src/articles/articles.module.ts (1)
14-14: Fix typo in variable name.There's a typo in the variable name that should be corrected for consistency.
- const tagsRepositry = new TagsRepository(db); + const tagsRepository = new TagsRepository(db);And update the reference on line 16:
- const tagsService = new TagsService(tagsRepositry); + const tagsService = new TagsService(tagsRepository);src/database/database.providers.ts (1)
13-21: Consider making logger configurable based on environmentThe database setup looks good. However, having the logger always enabled might impact performance in production environments.
Consider making the logger configurable:
+import { env } from '@/common/env'; export const db = drizzle(queryClient, { schema: { ...usersSchema, ...articlesSchema, ...tagsSchema, ...commentsSchema, }, - logger: true, + logger: env.NODE_ENV === 'development', });src/common/interfaces/api.interface.ts (1)
29-35: Consider adding HTTP method to ApiContextThe ApiContext interface provides comprehensive request information but is missing the HTTP method, which could be useful for request handling logic.
export type ApiContext = { userId?: number; headers: Headers; query: Record<string, string>; params: Record<string, string>; body: unknown; + method: 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH'; };src/app.module.ts (1)
68-68: Consider managing scalarVersion as a constantThe hardcoded scalar version might become outdated over time.
Consider moving the version to a constants file or package.json for easier maintenance:
+import { SCALAR_VERSION } from '@/common/constants'; // In swagger config - scalarVersion: '1.31.10', + scalarVersion: SCALAR_VERSION,src/articles/mappers/to-domain.mapper.ts (1)
8-34: Consider performance optimization for large datasets.The mapper uses multiple
some()operations which have O(n) complexity. For articles with many favorites or authors with many followers, this could impact performance.Consider pre-computing these relationships at the database level or using Sets for O(1) lookups:
export function toDomain( article: ArticleRow, { tagList, currentUserId }: ToDomainOptions, ): IArticle { + const favoritedBySet = currentUserId ? + new Set(article.favoritedBy.map(user => user.userId)) : + new Set(); + const followersSet = currentUserId ? + new Set(article.author.followers.map(f => f.followerId)) : + new Set(); + return { // ... other fields - favorited: currentUserId ? - article.favoritedBy.some((user) => user.userId === currentUserId) : - false, + favorited: currentUserId ? favoritedBySet.has(currentUserId) : false, // ... other fields author: { // ... other author fields - following: currentUserId ? - article.author.followers.some((follower) => follower.followerId === currentUserId) : - false, + following: currentUserId ? followersSet.has(currentUserId) : false, }, }; }drizzle/migrations/meta/0009_snapshot.json (1)
6-454: Consider adding database indexes for query performance.The schema lacks indexes on frequently queried columns which could impact performance as data grows.
Consider adding indexes for:
articles.author_id(foreign key queries)comments.article_id(comment retrieval by article)comments.author_id(author's comments queries)articles.created_at(chronological ordering)comments.created_at(comment ordering)These indexes would improve query performance for common operations.
src/comments/comments.controller.ts (1)
48-56: Simplify optional userId handling.The ternary operation to convert null to undefined is unnecessary complexity that could be simplified.
Apply this diff to simplify the logic:
async ({ params, store, request }) => { const userId = await store.authService.getOptionalUserIdFromHeader( request.headers, ); const comments = await store.commentsService.getComments( params.slug, - userId === null ? undefined : userId, + userId ?? undefined, ); return toCommentsResponse(comments); },src/auth/auth.service.ts (1)
73-88: Potential information disclosure in error messages.The detailed error messages could provide information to attackers about the expected authentication format. Consider using more generic messages for security.
Apply this diff to reduce information disclosure:
if (!rawHeader) throw new RealWorldError(StatusCodes.UNAUTHORIZED, { - Authorization: ['is missing'], + Authorization: ['Invalid authentication'], }); const tokenParts = rawHeader?.split(' '); const tokenType = tokenParts?.[0]; if (tokenType !== 'Token') throw new RealWorldError(StatusCodes.UNAUTHORIZED, { - token: ['is invalid. Expected header format: "Token jwt"'], + token: ['Invalid authentication format'], }); const token = tokenParts?.[1]; if (!token) throw new RealWorldError(StatusCodes.UNAUTHORIZED, { - token: ['is missing. Expected header format: "Token jwt"'], + token: ['Invalid authentication format'], });src/common/utils/validation.utils.ts (1)
37-40: Consider using a more comprehensive email validation regexThe current email regex pattern is quite basic and may not handle all valid email formats according to RFC 5322. Consider using a more comprehensive pattern or a dedicated email validation library for production use.
For better email validation coverage, consider this more comprehensive regex:
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;Or consider using a dedicated library like
validator.jsfor more robust email validation.src/common/errors/errors.utils.ts (1)
3-7: Track the temporary type definitionGood to see the comment acknowledging this is temporary. Consider creating a tracking issue to update this once Elysia exports the type officially.
Would you like me to create a GitHub issue to track updating this temporary type definition when Elysia exports it?
ARCHITECTURE.md (1)
153-153: Fix grammatical issue: Add comma after conjunctive adverb.The static analysis tool correctly identified a missing comma after the conjunctive adverb "Also".
-Also clearly service-level: it's a standardized rule, not controller-specific logic. +Also, clearly service-level: it's a standardized rule, not controller-specific logic.src/profiles/profiles.service.ts (1)
6-6: Address the TODO comment about UsersRepository removal.The TODO comment indicates uncertainty about why
UsersRepositorywas removed. Based on the architectural changes, this appears to be part of the modularization effort where each domain manages its own data access.The
UsersRepositorywas likely removed to maintain clear separation of concerns - the profiles domain should handle profile-related operations throughProfilesRepository, which can access user data via the database schema relations. This aligns with the layered architecture documented inARCHITECTURE.md.Would you like me to help clarify this architectural decision or remove the TODO comment?
src/articles/articles.repository.ts (1)
239-253: Consider implementing transaction-based favorite operationsThe TODO comment correctly identifies that these operations should be atomic. The current implementation performs 2-3 separate queries which could lead to race conditions or inconsistent state.
Would you like me to implement a transaction-based solution that combines these operations into a single atomic operation, or create an issue to track this optimization?
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
bun.lockis excluded by!**/*.lockbun.lockbis excluded by!**/bun.lockb
📒 Files selected for processing (107)
.vscode/settings.json(1 hunks)ARCHITECTURE.md(1 hunks)CODE_OF_CONDUCT.md(2 hunks)CONTRIBUTING.md(3 hunks)GOVERNANCE.md(1 hunks)PROJECT_STRUCTURE.md(1 hunks)README.md(3 hunks)db/drop.ts(0 hunks)docs/dev-container.md(2 hunks)docs/getting-started.md(2 hunks)docs/index.md(2 hunks)docs/what-is-bedstack.md(2 hunks)drizzle.config.ts(2 hunks)drizzle/migrations/0009_ambitious_matthew_murdock.sql(1 hunks)drizzle/migrations/meta/0009_snapshot.json(1 hunks)drizzle/migrations/meta/_journal.json(1 hunks)drizzle/reset.ts(1 hunks)drizzle/seed.ts(1 hunks)package.json(2 hunks)src/app.module.ts(2 hunks)src/articles/articles.controller.ts(1 hunks)src/articles/articles.model.ts(0 hunks)src/articles/articles.module.ts(1 hunks)src/articles/articles.plugin.ts(0 hunks)src/articles/articles.repository.ts(3 hunks)src/articles/articles.schema.ts(1 hunks)src/articles/articles.service.ts(3 hunks)src/articles/comments/comments.schema.ts(0 hunks)src/articles/comments/comments.service.ts(0 hunks)src/articles/dto/article-feed-query.dto.ts(1 hunks)src/articles/dto/article-response.dto.ts(1 hunks)src/articles/dto/articles-response.dto.ts(1 hunks)src/articles/dto/create-article.dto.ts(1 hunks)src/articles/dto/index.ts(1 hunks)src/articles/dto/list-articles-query.dto.ts(1 hunks)src/articles/dto/update-article.dto.ts(1 hunks)src/articles/interfaces/article-feed-row.interface.ts(1 hunks)src/articles/interfaces/article-feed.interface.ts(1 hunks)src/articles/interfaces/article-row.interface.ts(1 hunks)src/articles/interfaces/article.interface.ts(1 hunks)src/articles/interfaces/create-article-input.interface.ts(1 hunks)src/articles/interfaces/index.ts(1 hunks)src/articles/interfaces/new-article-row.interface.ts(1 hunks)src/articles/interfaces/update-article-input.interface.ts(1 hunks)src/articles/interfaces/update-article-row.interface.ts(1 hunks)src/articles/mappers/index.ts(1 hunks)src/articles/mappers/to-create-article-input.mapper.ts(1 hunks)src/articles/mappers/to-domain.mapper.ts(1 hunks)src/articles/mappers/to-feed-domain.mapper.ts(1 hunks)src/articles/mappers/to-feed-response.mapper.ts(1 hunks)src/articles/mappers/to-new-article-row.mapper.ts(1 hunks)src/articles/mappers/to-response.mapper.ts(1 hunks)src/auth/auth.service.ts(4 hunks)src/comments/comments.controller.ts(1 hunks)src/comments/comments.module.ts(1 hunks)src/comments/comments.repository.ts(2 hunks)src/comments/comments.schema.ts(1 hunks)src/comments/comments.service.ts(1 hunks)src/comments/dto/comment-response.dto.ts(1 hunks)src/comments/dto/comments-response.dto.ts(1 hunks)src/comments/dto/create-comment.dto.ts(1 hunks)src/comments/dto/index.ts(1 hunks)src/comments/interfaces/comment-row.interface.ts(1 hunks)src/comments/interfaces/comment.interface.ts(1 hunks)src/comments/interfaces/index.ts(1 hunks)src/comments/interfaces/new-comment-row.interface.ts(1 hunks)src/comments/mappers/index.ts(1 hunks)src/comments/mappers/to-comment-response.mapper.ts(1 hunks)src/comments/mappers/to-comments-response.mapper.ts(1 hunks)src/comments/mappers/to-domain.mapper.ts(1 hunks)src/comments/mappers/to-new-comment-row.mapper.ts(1 hunks)src/common/constants/errors.constants.ts(1 hunks)src/common/constants/index.ts(1 hunks)src/common/constants/pagination.constants.ts(1 hunks)src/common/errors/errors.utils.ts(1 hunks)src/common/errors/index.ts(1 hunks)src/common/errors/realworld.error.ts(1 hunks)src/common/interfaces/api.interface.ts(1 hunks)src/common/interfaces/common.interface.ts(1 hunks)src/common/interfaces/index.ts(1 hunks)src/common/utils/date.utils.ts(1 hunks)src/common/utils/index.ts(1 hunks)src/common/utils/validation.utils.ts(1 hunks)src/constants.ts(0 hunks)src/database.providers.ts(0 hunks)src/database/database.providers.ts(1 hunks)src/errors.ts(0 hunks)src/profiles/dto/index.ts(1 hunks)src/profiles/dto/profile-response.dto.ts(1 hunks)src/profiles/interfaces/index.ts(1 hunks)src/profiles/interfaces/profile-row.interface.ts(1 hunks)src/profiles/interfaces/profile.interface.ts(1 hunks)src/profiles/mappers/index.ts(1 hunks)src/profiles/mappers/to-domain.mapper.ts(1 hunks)src/profiles/mappers/to-response.mapper.ts(1 hunks)src/profiles/profiles.controller.ts(1 hunks)src/profiles/profiles.module.ts(1 hunks)src/profiles/profiles.plugin.ts(0 hunks)src/profiles/profiles.repository.ts(2 hunks)src/profiles/profiles.schema.ts(0 hunks)src/profiles/profiles.service.ts(1 hunks)src/tags/dto/index.ts(1 hunks)src/tags/dto/tags-response.dto.ts(1 hunks)src/tags/interfaces/index.ts(1 hunks)src/tags/interfaces/tag-row.interface.ts(1 hunks)src/tags/interfaces/tag.interface.ts(1 hunks)src/tags/mappers/index.ts(1 hunks)
⛔ Files not processed due to max files limit (33)
- src/tags/mappers/to-domain.mapper.ts
- src/tags/mappers/to-response.mapper.ts
- src/tags/tags.controller.ts
- src/tags/tags.model.ts
- src/tags/tags.module.ts
- src/tags/tags.plugin.ts
- src/tags/tags.repository.ts
- src/tags/tags.schema.ts
- src/tags/tags.service.ts
- src/users/dto/create-user.dto.ts
- src/users/dto/index.ts
- src/users/dto/login-user.dto.ts
- src/users/dto/update-user.dto.ts
- src/users/dto/user-response.dto.ts
- src/users/index.ts
- src/users/interfaces/create-user-input.interface.ts
- src/users/interfaces/index.ts
- src/users/interfaces/update-user-input.interface.ts
- src/users/interfaces/user-follow-row.interface.ts
- src/users/interfaces/user-row.interface.ts
- src/users/interfaces/user.interface.ts
- src/users/mappers/index.ts
- src/users/mappers/to-domain.mapper.ts
- src/users/mappers/to-response.mapper.ts
- src/users/mappers/to-row.mapper.ts
- src/users/users.controller.ts
- src/users/users.model.ts
- src/users/users.module.ts
- src/users/users.plugin.ts
- src/users/users.repository.ts
- src/users/users.schema.ts
- src/users/users.service.ts
- tsconfig.json
💤 Files with no reviewable changes (10)
- src/constants.ts
- src/database.providers.ts
- src/profiles/profiles.schema.ts
- src/articles/comments/comments.schema.ts
- src/profiles/profiles.plugin.ts
- db/drop.ts
- src/articles/articles.plugin.ts
- src/errors.ts
- src/articles/articles.model.ts
- src/articles/comments/comments.service.ts
🧰 Additional context used
🪛 LanguageTool
docs/getting-started.md
[grammar] ~49-~49: It seems that a pronoun is missing.
Context: ...oders/bedstack/blob/main/SUPPORT.md). * If have any questions or just want to chat...
(IF_VB)
GOVERNANCE.md
[uncategorized] ~30-~30: The noun “decision-making” (= the process of deciding something) is spelled with a hyphen.
Context: ...s not always public, the results of the decision making process must be public. Decisions will ...
(DECISION_MAKING)
CODE_OF_CONDUCT.md
[style] ~32-~32: Try using a synonym here to strengthen your wording.
Context: ...ind - Trolling, insulting or derogatory comments, and personal or political attacks - Pu...
(COMMENT_REMARK)
CONTRIBUTING.md
[uncategorized] ~30-~30: This expression is usually spelled with a hyphen.
Context: ... and formatting**. It is an ultra-fast, Rust based linter and formatter. It also lints JSO...
(BASED_HYPHEN)
docs/what-is-bedstack.md
[uncategorized] ~5-~5: Possible missing comma found.
Context: ...d flexible enough to handle complex use cases as demonstrated by this [RealWorld](htt...
(AI_HYDRA_LEO_MISSING_COMMA)
PROJECT_STRUCTURE.md
[style] ~93-~93: Using many exclamation marks might seem excessive (in this case: 5 exclamation marks for a text that’s 2616 characters long)
Context: ...to common/ only when it's reused. > [!TIP] > Only one constants/ folder exis...
(EN_EXCESSIVE_EXCLAMATION)
[style] ~115-~115: As an alternative to the over-used intensifier ‘very’, consider replacing this phrase.
Context: ...e relations() in the same file unless very large - If split, name the second file `featu...
(EN_WEAK_ADJECTIVE)
ARCHITECTURE.md
[uncategorized] ~153-~153: A comma may be missing after the conjunctive/linking adverb ‘Also’.
Context: ...turn email.toLowerCase().trim(); } ``` Also clearly service-level: it's a standardi...
(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)
🪛 markdownlint-cli2 (0.17.2)
docs/dev-container.md
49-49: Unordered list style
Expected: dash; Actual: asterisk
(MD004, ul-style)
🔇 Additional comments (118)
docs/index.md (2)
27-29: Clean up trailing whitespace
Removed extra spaces after theicon:key to enforce consistent markdown formatting.
76-76: Ensure no trailing blank lines
Standardized file ending by removing extra blank line.README.md (8)
12-12: Remove heading trailing space
Trimmed the trailing whitespace in the Discord invitation heading for consistency.
28-32: Normalize code block indentation
Converted the indented env-copy snippet to a fenced code block aligned with backticks.
36-38: Update snippet formatting
Standardized the Docker startup snippet as a fenced code block.
40-44: Format migration command
Converted the Postgres migration step to a fenced code block for visual consistency.
46-50: Fence development server command
Reformatted thebun devstep into a proper fenced code block.
52-56: Fence test command
Standardized the API test invocation as a fenced code snippet.
65-67: Fence build command
Aligned the production build step (bun run build) to the fenced code block style.
71-73: Fence preview command
Reformatted thebun previewcommand into a fenced code block.CONTRIBUTING.md (2)
12-14: Standardize installation snippet
Changed the indented shell snippet for Bun installation to a top-level fenced code block.
72-75: Unify bullet style
Replaced asterisks with hyphens in the issue-closing keywords list for consistent markdown styling.CODE_OF_CONDUCT.md (3)
20-23: Add list prefixes for clarity
Inserted leading hyphens on existing list items under “Our Standards” to ensure they render correctly.
25-27: Add list prefixes for clarity
Inserted leading hyphens on the remaining standard examples to match list formatting.
109-109: Tighten punctuation spacing
Removed the extra space after the comma in the “Permanent Ban” clause to tighten sentence structure.docs/what-is-bedstack.md (5)
5-6: Consolidate introductory text
Merged and reformatted the opening paragraph links for readability.
7-7: Reformat spec reference
Reflowed the RealWorld spec sentence into its own line for consistency.
13-13: Standardize feature list item
Removed trailing spaces on the Bun feature bullet for uniform list formatting.
15-15: Standardize feature list item
Removed trailing spaces on the ElysiaJS feature bullet for consistency.
17-17: Standardize feature list item
Removed trailing spaces on the Drizzle feature bullet to align list styling.docs/getting-started.md (2)
11-14: LGTM: Formatting improvements enhance consistency.The bullet point style changes from asterisks to hyphens improve markdown consistency across the documentation.
28-30: LGTM: Code block formatting standardized.Removing leading spaces from code blocks improves readability and follows standard markdown formatting practices.
Also applies to: 34-36, 40-42
docs/dev-container.md (2)
13-15: LGTM: Bullet point formatting standardized.The change from asterisks to hyphens for bullet points improves consistency with the project's markdown style.
41-43: LGTM: Indentation improvements enhance readability.The adjusted indentation for the numbered sub-steps improves the visual hierarchy and readability.
GOVERNANCE.md (1)
7-8: LGTM: Formatting improvements enhance document structure.The bullet point style standardization and indentation adjustments improve the document's readability and consistency with the project's markdown style guidelines.
Also applies to: 16-34
src/common/constants/pagination.constants.ts (1)
1-29: LGTM: Well-designed pagination constants with excellent documentation.This implementation demonstrates several best practices:
- Clear, descriptive constant names using SCREAMING_SNAKE_CASE
- Comprehensive JSDoc documentation explaining the rationale behind values
- Sensible default values (20 items) with appropriate limits (max 100)
- Clear explanation of offset-based vs page-based pagination choice
- Good separation of concerns by centralizing pagination configuration
The constants provide a solid foundation for consistent pagination behavior across the application.
src/common/interfaces/common.interface.ts (1)
1-63: LGTM: Excellent foundation for type-safe development.This interface file demonstrates outstanding TypeScript practices:
Strengths:
- Clear separation of concerns: Each interface has a single, well-defined responsibility
- Comprehensive documentation: JSDoc comments explain the purpose and usage of each type
- Type safety: Strong typing throughout with appropriate generic constraints
- Reusability: Generic types like
PaginatedResult<T>can be used across different domains- Database integration: Smart use of Drizzle ORM types with the
InferNewRowutility- Modern TypeScript: Leverages advanced features like conditional types and mapped types
Notable implementations:
BaseEntityfollows standard entity patterns with audit fieldsInferNewRowcleverly omits system fields from insert operationsPrettifyutility improves IDE type display experienceFilterParamsprovides flexible filtering capabilityThis provides an excellent foundation for the layered architecture and supports consistent API patterns across the application.
drizzle/migrations/meta/_journal.json (1)
67-74: Migration journal entry is correctly ordered and formatted.The new entry for
0009_ambitious_matthew_murdockfollows the existingidxandversionsequence and maintains the JSON structure. No issues detected.src/common/constants/errors.constants.ts (1)
1-1: Introduce a centralized default error message.
DEFAULT_ERROR_MESSAGEprovides a consistent fallback across error handlers. Ensure it's utilized in your global error utilities to standardize user-facing messages..vscode/settings.json (1)
18-20: Update VS Code workspace settings.Adding domain-specific spellcheck words and specifying
typescript.tsdkaligns the editor with the project’s conventions and ensures consistent tooling.src/tags/dto/index.ts (1)
1-1: Barrel export for tag DTOs.Re-exporting
tags-response.dtohere cleanly aggregates DTOs and mirrors the pattern used in other modules.src/common/interfaces/index.ts (1)
1-2: Aggregate common interfaces in a single barrel.This barrel module simplifies imports for
common.interfaceandapi.interface, enhancing modularity and consistency.src/comments/dto/index.ts (1)
1-3: Approve barrel export for comment DTOs.This index file correctly consolidates all comment DTO exports, matching the established pattern and improving import ergonomics.
src/profiles/mappers/index.ts (1)
1-2: Approve barrel export for profile mappers.The file cleanly re-exports both domain and response mappers, consistent with other feature layers.
src/profiles/dto/index.ts (1)
1-1: Approve barrel export for profile DTOs.Centralizing profile-response DTO exports in a single entry point aligns with the layered architecture conventions.
src/tags/mappers/index.ts (1)
1-2: Approve barrel export for tag mappers.This index module correctly aggregates tag-to-domain and tag-to-response mappers, mirroring other domains.
src/common/constants/index.ts (1)
1-2: Approve barrel export for shared constants.Re-exporting pagination and error constants here simplifies shared imports and adheres to project structure.
src/common/errors/index.ts (1)
1-2: Centralized error exports look good.This index cleanly aggregates the
RealWorldErrorand associated utilities for unified error handling across the application.src/tags/interfaces/index.ts (1)
1-2: Interface re-exports are correctly consolidated.This index provides a single entry point for tag-related interfaces, aligning with the layered architecture conventions.
src/comments/interfaces/comment-row.interface.ts (1)
1-4: Type alias for CommentRow is clear and type-safe.Leveraging
InferSelectModelensures the row shape stays in sync with the schema.src/profiles/interfaces/index.ts (1)
1-2: Profile interfaces re-export is consistent.This index aligns with other feature directories and simplifies imports for profile-related types.
src/comments/interfaces/index.ts (1)
1-3: Comments interfaces properly aggregated.The index centralizes exports for comment, comment-row, and new-comment-row interfaces, matching the project’s modular structure.
src/comments/interfaces/new-comment-row.interface.ts (1)
1-4: Appropriate type alias for new comment rows.The
NewCommentRowalias correctly infers the shape from thecommentsschema, promoting type safety in the repository and service layers.drizzle/migrations/0009_ambitious_matthew_murdock.sql (1)
1-4: Ensure application handles nullablebioandimagefields.Dropping the NOT NULL constraint allows
bioandimageto be null, which may require updates to the User entity and downstream DTOs or mappers to handle optional values gracefully.Please verify all usages of
bioandimagein the codebase and update interfaces or mappers if needed.src/articles/interfaces/update-article-row.interface.ts (1)
1-3: Correct partial type for article updates.The
UpdateArticleRowalias appropriately wrapsNewArticleRowinPartial<>to allow optional properties during update operations.src/articles/interfaces/new-article-row.interface.ts (1)
1-4: Type alias infers new article row correctly.
NewArticleRowis correctly defined usingInferNewRow<typeof articles>, aligning the database schema with TypeScript types for creation operations.src/articles/interfaces/create-article-input.interface.ts (1)
1-6: CreateArticleInput interface matches API spec.The interface clearly defines the required fields (
title,description,body,tagList) for article creation requests.drizzle.config.ts (3)
3-10: LGTM: Improved encapsulation.Making
dbCredentialslocal instead of exported improves encapsulation and reduces the module's public API surface.
15-17: LGTM: Consistent with architectural refactoring.The changes to output directory (
./drizzle/migrations) and schema pattern (.schema.ts) align well with the layered architecture refactoring mentioned in the PR objectives.
20-20: LGTM: Clean shorthand property notation.Using shorthand property notation for
dbCredentialsis more concise and follows modern JavaScript/TypeScript best practices.src/profiles/interfaces/profile-row.interface.ts (1)
1-6: LGTM: Well-designed interface with clear type composition.The
ProfileRowinterface effectively uses TypeScript's intersection types to extendUserRowwith followers information. The type composition is clean and follows best practices for modular type design.src/articles/dto/update-article.dto.ts (1)
4-7: LGTM: Proper use of partial types for update operations.The DTO correctly uses
t.Partial()to make all fields optional for update operations, which is the appropriate pattern for PATCH/PUT endpoints. The dual export of both schema and type follows Elysia best practices.src/articles/mappers/to-feed-domain.mapper.ts (1)
3-15: LGTM: Clean mapper implementation following the pattern.The
toFeedDomainmapper correctly implements the transformation from database row to domain object. The direct property mapping is appropriate and the function follows the pure function pattern, making it predictable and testable.src/comments/mappers/index.ts (1)
1-4: LGTM! Consistent barrel export pattern.The index file correctly consolidates all comment mapper exports, following the established architectural pattern across the codebase. This promotes clean imports and maintains modularity.
src/profiles/interfaces/profile.interface.ts (1)
1-6: Well-defined domain interface.The
IProfileinterface correctly models a user profile with appropriate nullable fields for optional data (bio, image) and required fields for essential data (username, following status). The type definition aligns well with the domain requirements.src/common/utils/index.ts (1)
1-4: Good consolidation of utility functions.The index file appropriately centralizes common utility exports, providing a clean single entry point for utility functions across the application. This supports the modular architecture goals.
src/profiles/mappers/to-response.mapper.ts (1)
4-18: Excellent mapper implementation with proper documentation.The
toResponsefunction is well-implemented with clear JSDoc documentation and correct transformation from domain model to response DTO. The nesting of profile data under theprofilekey follows API design conventions, and all fields are properly mapped.src/tags/dto/tags-response.dto.ts (1)
1-8: LGTM! Well-structured DTO implementation.The implementation correctly uses Elysia's validation schema and follows the expected pattern for response DTOs. The schema properly defines tags as an array of strings, and the type export is correctly implemented.
src/articles/dto/index.ts (1)
1-7: LGTM! Standard barrel file implementation.The barrel file correctly consolidates all article DTOs with consistent export patterns, making imports cleaner for consumers.
src/comments/mappers/to-comments-response.mapper.ts (1)
1-9: LGTM! Well-structured mapper implementation.The mapper correctly transforms an array of comment entities to the expected response format, properly reusing the existing
toCommentResponsemapper and extracting the nested comment data.src/articles/interfaces/update-article-input.interface.ts (1)
1-7: LGTM! Well-designed interface for partial updates.The interface correctly defines all properties as optional, which is appropriate for update operations where users may want to modify only specific fields.
src/comments/dto/create-comment.dto.ts (1)
1-9: LGTM! Well-structured DTO with proper validation.The DTO correctly implements the nested structure for comment creation with appropriate validation. The minimum length constraint ensures comments are not empty, which is good for data quality.
drizzle/seed.ts (1)
3-8: Import restructuring aligns well with the new layered architecture.The updated import paths correctly reflect the modular organization by domain (articles, comments, tags, users) and the new database provider location.
src/comments/dto/comments-response.dto.ts (1)
4-6: Verify the property access pattern for nested DTO references.The usage of
CommentResponseDto.properties.commentassumes thatCommentResponseDtohas acommentproperty that can be accessed via.properties. This pattern could be fragile if the referenced DTO structure changes.Consider documenting this dependency or using a more explicit approach:
+// Assumes CommentResponseDto has structure: { comment: CommentSchema } export const CommentsResponseDto = t.Object({ comments: t.Array(CommentResponseDto.properties.comment), });Let me verify the structure of
CommentResponseDtoto ensure this property access is valid:#!/bin/bash # Description: Verify the structure of CommentResponseDto to ensure property access is valid # Expected: Find CommentResponseDto definition with a 'comment' property fd -e ts -e js | xargs rg -l "CommentResponseDto" | head -5 rg -A 10 "export.*CommentResponseDto.*=" --type tssrc/articles/mappers/to-response.mapper.ts (1)
4-25: LGTM! Clean and comprehensive mapping implementation.The mapper function correctly transforms domain objects to response DTOs with proper:
- Date formatting using ISO strings
- Author profile extraction with explicit field mapping
- Structured response format with nested
articlepropertyThe implementation follows good practices for data transformation in a layered architecture.
src/articles/interfaces/article-row.interface.ts (1)
6-10: LGTM! Well-structured interface for enriched article data.The interface correctly:
- Uses Drizzle ORM's
InferSelectModelfor type inference- Omits
authorIdwhen including the fullauthorobject to avoid redundancy- Properly types related entities (author, favorites, tags)
- Maintains consistency with the modular architecture
This design supports efficient database queries with joined relations while maintaining type safety.
src/articles/dto/list-articles-query.dto.ts (1)
4-12: LGTM! Excellent use of DTO composition for query parameters.The implementation demonstrates good practices:
- Uses
t.Compositeto extend existingArticleFeedQueryDtofunctionality- Applies appropriate validation with
minLength: 1to prevent empty filter strings- Includes logical filtering options (tag, author, favorited) for article queries
- Maintains clean separation between pagination and filtering concerns
This approach promotes reusability and maintains consistency across the API.
src/comments/mappers/to-domain.mapper.ts (1)
10-34: LGTM! Excellent mapper implementation with clear documentation.The function demonstrates best practices:
- Comprehensive JSDoc documentation explaining parameters and return value
- Custom type
ToCommentsDomainAuthorprovides clear contract for author data- Complete field mapping from database row to domain object
- Proper handling of timestamps and author relationship
- Clean separation of concerns between data transformation and business logic
This mapper aligns well with the layered architecture pattern and provides a clear transformation boundary.
src/profiles/profiles.module.ts (1)
1-12: LGTM! Clean refactoring with improved consistency.The changes demonstrate good architectural improvements:
- Standardized import paths using
@/prefix for consistency- Simplified service dependency injection by removing
UsersRepository- Fixed the typo in variable naming
- Clean module setup pattern
src/articles/interfaces/article.interface.ts (1)
3-13: Well-structured interface with comprehensive type coverage.The
IArticleinterface provides excellent type safety with all necessary article fields properly typed. The structure supports the layered architecture pattern effectively.src/articles/interfaces/article-feed-row.interface.ts (1)
4-17: Excellent use of type composition and utility types.The interface demonstrates good TypeScript practices:
- Leverages Drizzle ORM's
InferSelectModelfor schema consistency- Properly omits fields not needed in feed context
- Clean extension with additional computed fields
- Well-structured author object with appropriate fields
src/articles/mappers/to-feed-response.mapper.ts (1)
4-23: Well-implemented mapper function with clean transformation logic.The function demonstrates good practices:
- Clear parameter destructuring with proper typing
- Efficient array mapping with field selection
- Proper date formatting using
toISOString()- Smart default handling for
articlesCountusing nullish coalescing- Pure function design with no side effects
src/profiles/mappers/to-domain.mapper.ts (1)
4-25: Well-designed domain mapper with proper relationship handling.The function demonstrates solid domain mapping practices:
- Clear separation between data and domain layers
- Proper null safety handling for
currentUserId- Efficient follower relationship computation using
Array.some()- Good JSDoc documentation
- Clean, readable logic flow
src/comments/dto/comment-response.dto.ts (1)
1-19: Well-structured DTO with proper type safety.The implementation follows best practices for Elysia DTOs:
- Proper use of
t.Union([t.Null(), t.String()])for nullable fields- Clear nested object structure matching the domain model
- Export of both schema and static type for runtime and compile-time validation
src/common/utils/date.utils.ts (1)
1-32: Excellent utility functions with comprehensive documentation.The implementations are correct and well-documented:
formatDateToISOprovides a clear wrapper aroundtoISOString()isValidDateproperly handles invalid Date objects usingNumber.isNaN(date.getTime())getCurrentDateISOoffers a convenient current timestamp function- JSDoc examples enhance developer experience
src/articles/articles.module.ts (2)
4-4: Correct database provider import path.The import path update from
@/database.providersto@/database/database.providersaligns with the new project structure.
15-15: Clean separation of concerns achieved.The simplified
ProfilesServiceconstructor reflects the proper separation where comments functionality has been extracted to its own module. This aligns well with the layered architecture goals.src/comments/interfaces/comment.interface.ts (1)
1-12: Well-defined domain interface with proper type distinction.The
ICommentinterface correctly defines the domain model structure:
- Uses
Datetypes for timestamps (appropriate for internal domain model)- Nullable fields for optional author information
- Clean separation from the DTO which uses string types for API serialization
This demonstrates good architectural separation between domain models and API contracts.
src/tags/interfaces/tag-row.interface.ts (1)
1-9: LGTM! Clean interface definitions following Drizzle ORM patterns.The interface definitions properly leverage Drizzle ORM's type inference utilities and follow a consistent naming convention. The separation between existing row types (
TagRow,ArticleTagRow) and new row types (NewTagRow,NewArticleTagRow) provides clear contracts for database operations.src/profiles/dto/profile-response.dto.ts (1)
1-12: LGTM! Well-structured DTO with appropriate nullable fields.The DTO correctly models the profile response structure with appropriate nullable types for optional fields like
bioandimage. The use of Elysia's type system provides both runtime validation and compile-time type safety.src/articles/dto/articles-response.dto.ts (1)
5-5: Verify the omitted 'body' field exists in ArticleResponseDto.Ensure that the
ArticleResponseDto.properties.articleactually contains abodyfield to avoid runtime errors.#!/bin/bash # Description: Verify that ArticleResponseDto contains a 'body' field in its article property # Expected: Find the 'body' field definition in the ArticleResponseDto schema # Search for ArticleResponseDto definition and its properties rg -A 10 -B 5 "ArticleResponseDto.*=.*t\.Object" src/articles/dto/src/articles/interfaces/article-feed.interface.ts (1)
1-13: LGTM! Comprehensive interface with proper type relationships.The
IArticleFeedinterface provides a complete structure for article feed items with appropriate types. The cross-module reference toIProfiledemonstrates good architectural separation and reusability.src/articles/mappers/index.ts (1)
1-6: LGTM! Clean barrel export pattern for mapper organization.The barrel export approach provides a clean import interface for all article mappers, supporting the modular architecture by centralizing mapper exports.
package.json (3)
24-29: LGTM! Database script simplification aligns with reorganization.The removal of explicit
--config=db/config.tsflags and path changes fromdb/todrizzle/directory are consistent with the architectural refactoring. The replacement ofdb:dropwithdb:resetprovides clearer semantics.
32-41: New dependencies support the layered architecture well.The addition of authentication (
@elysiajs/bearer,@elysiajs/jwt), validation (@sinclair/typebox), HTTP utilities (http-status-codes), and functional utilities (radashi) aligns with the modular architecture goals. Dependency upgrades are also appropriate.
54-57: ```shell
#!/bin/bashDescription: Search for version specifiers of @sinclair/typebox across the repository
echo "Searching for version specifiers of @sinclair/typebox:"
rg -n "@sinclair/typebox@"</details> <details> <summary>src/articles/dto/article-response.dto.ts (1)</summary> `1-22`: **Well-structured DTO with proper validation and typing.** The implementation demonstrates excellent practices: - Clean separation of runtime validation and TypeScript types - Proper handling of nullable fields with `t.Union([t.Null(), t.String()])` - Consistent nested object structure - All expected article response fields included The schema aligns well with the layered architecture approach. </details> <details> <summary>src/tags/interfaces/tag.interface.ts (1)</summary> `1-12`: **Clean and well-designed domain interfaces.** The type definitions are excellent: - Clear separation between `ITag` (tag entity) and `IArticleTag` (relationship) - Proper timestamp handling with `Date` type - Consistent naming conventions - Follows domain-driven design principles These interfaces provide solid foundation for the tags domain. </details> <details> <summary>src/comments/comments.schema.ts (1)</summary> `1-30`: **Excellent Drizzle ORM schema implementation.** The schema demonstrates best practices: - Proper foreign key relationships with cascade delete (appropriate for comments) - Clean table structure with serial primary key and timestamps - Well-defined relational mappings with custom relation names - Correct import paths using alias resolution The cascade delete behavior ensures data integrity when articles or users are removed. </details> <details> <summary>src/articles/interfaces/index.ts (1)</summary> `1-8`: **Perfect barrel export implementation.** This consolidation file follows excellent practices: - Clean wildcard re-exports for all article interfaces - Consistent naming conventions across interface files - Enables centralized imports for the articles domain - Supports the modular layered architecture This approach will improve maintainability and developer experience. </details> <details> <summary>src/comments/comments.module.ts (1)</summary> `13-35`: **Clean layered architecture implementation!** The module setup follows proper dependency injection patterns with clear separation of concerns. Repositories are instantiated first, followed by services with their dependencies properly injected. </details> <details> <summary>src/articles/dto/article-feed-query.dto.ts (1)</summary> `16-34`: **Well-structured DTO with comprehensive validation!** The DTO properly validates pagination parameters with clear constraints and helpful descriptions for API documentation. Good use of shared constants for consistency. </details> <details> <summary>src/app.module.ts (1)</summary> `23-48`: **Excellent error handling implementation!** The error handler properly differentiates between various error types and formats them appropriately. Good use of the pick utility to prevent leaking internal error details. </details> <details> <summary>src/articles/mappers/to-domain.mapper.ts (1)</summary> `20-22`: **Verify null safety in favorited calculation.** The `some()` operation doesn't handle the case where `currentUserId` is undefined, which could lead to incorrect favorited status. Apply this diff to ensure proper null safety: ```diff - favorited: article.favoritedBy.some( - (user) => user.userId === currentUserId, - ), + favorited: currentUserId ? + article.favoritedBy.some((user) => user.userId === currentUserId) : + false,Likely an incorrect or invalid review comment.
src/comments/comments.repository.ts (2)
9-15: LGTM! Improved null handling in create method.The explicit null coalescing and updated type usage improves type safety and consistency with the new architecture.
58-64: Verify delete method behavior with concurrent operations.The delete method now returns a boolean based on the number of deleted rows. Consider potential race conditions if the comment is deleted by another process between the check and deletion.
Run the following script to verify if there are any concurrent comment deletion patterns in the codebase:
#!/bin/bash # Description: Check for concurrent comment deletion patterns and transaction usage # Search for comment deletion patterns rg -A 5 -B 5 "delete.*comment" --type ts # Check for transaction usage in comment operations rg -A 3 -B 3 "transaction.*comment|comment.*transaction" --type tssrc/comments/comments.controller.ts (2)
78-81: LGTM! Proper parameter validation for delete endpoint.The explicit parameter validation with
t.Numeric()for the comment ID ensures type safety and prevents invalid requests.
10-96: Verify error handling consistency across endpoints.The controller doesn't explicitly handle service-level errors. Ensure that the service methods throw appropriate
RealWorldErrorinstances for consistent error responses.Run the following script to verify error handling patterns in the comments service:
#!/bin/bash # Description: Verify error handling patterns in comments service # Check for RealWorldError usage in comments service ast-grep --pattern 'throw new RealWorldError($_)' # Check for try-catch blocks in service methods ast-grep --pattern 'try { $$$ } catch ($$$) { $$$ }'src/auth/auth.service.ts (2)
58-65: LGTM! Improved structured error handling.The transition to
RealWorldErrorwith HTTP status codes and structured error details provides better consistency across the application.
112-117: LGTM! Improved optional authentication handling.The change from
nulltoundefinedfor optional authentication provides better TypeScript consistency and the error filtering logic is sound.PROJECT_STRUCTURE.md (1)
1-124: Excellent documentation of the project structure!The documentation clearly explains the flat, feature-sliced architecture with self-contained domain features. The structure aligns well with the layered architecture pattern and provides good guidance for maintaining consistency across the codebase.
src/common/utils/validation.utils.ts (1)
1-57: Well-structured validation utilities!The validation functions are properly documented with JSDoc comments and examples. The use of type predicates in
isNullOrUndefinedand the URL constructor approach inisValidUrlare particularly good practices.src/articles/articles.controller.ts (1)
1-226: Well-structured controller implementation!The controller properly implements RESTful endpoints with:
- Consistent authentication handling
- Proper request/response validation using DTOs
- Clear route organization with detailed OpenAPI metadata
- Appropriate HTTP status codes (201 for creation, 204 for deletion)
src/profiles/profiles.controller.ts (2)
1-86: Clean controller implementation!The controller properly delegates business logic to the service layer, uses consistent response mapping, and includes comprehensive OpenAPI documentation. The authentication and authorization checks are appropriately placed.
15-36: ```shell
#!/bin/bashLocate the ProfilesService implementation and show the definition of findProfileByUsername
service_file=$(fd profiles.service.ts src | head -n1)
echo "ProfilesService file: $service_file"
rg -A5 -B5 "findProfileByUsername" -n "$service_file"</details> <details> <summary>src/common/errors/errors.utils.ts (1)</summary> `1-71`: **Excellent error handling utilities!** The error formatting functions provide consistent, user-friendly error responses with: - Proper path normalization (URL to dot notation) - Message deduplication - Redundant prefix removal for cleaner messages - Edge case handling for empty validation errors </details> <details> <summary>ARCHITECTURE.md (1)</summary> `1-162`: **Excellent architecture documentation that provides clear guidance.** This comprehensive architecture document effectively establishes the layered architecture principles and provides concrete guidance for the codebase refactoring. The documentation includes: - Clear separation of concerns across controller, service, and repository layers - Helpful Mermaid diagram showing relationships between components - Practical examples demonstrating business logic placement - Type design principles for interfaces vs classes and canonical forms The examples and layer responsibilities align well with the actual implementation changes visible in the other files under review. </details> <details> <summary>src/articles/articles.schema.ts (3)</summary> `14-25`: **Well-structured articles table with proper constraints.** The table definition follows PostgreSQL best practices with: - Serial primary key for performance - Unique constraint on slug for SEO-friendly URLs - Not null constraints on required fields - Proper foreign key constraint with cascade delete - Timestamps with default values The cascade delete on `authorId` ensures data integrity when users are deleted. --- `27-40`: **Proper many-to-many relationship design for favorites.** The `favoriteArticles` join table correctly implements: - Composite primary key preventing duplicate favorites - Foreign key constraints with cascade delete to maintain referential integrity - Timestamps for audit trail - Proper naming convention This design efficiently handles the many-to-many relationship between users and their favorite articles. --- `42-73`: **Well-defined relations for comprehensive data fetching.** The relations definitions properly establish: - Bidirectional relationships between articles and related entities - Consistent relation names for query clarity - Proper field and reference mappings - Support for eager loading of related data This enables efficient queries with Drizzle ORM's relational query API. </details> <details> <summary>src/comments/comments.service.ts (2)</summary> `17-46`: **Solid comment creation logic with proper validation and error handling.** The `createComment` method correctly: - Validates article existence before creating comment - Uses mappers for data transformation between layers - Handles repository failure with appropriate error responses - Fetches author profile for complete domain model - Sets following status correctly (false for self) The error handling properly uses `RealWorldError` with HTTP status codes. --- `54-80`: **Efficient comment retrieval with proper domain mapping.** The `getComments` method: - Validates article existence first - Retrieves comments efficiently from repository - Maps each comment to domain model with author details - Correctly determines following status based on current user - Handles optional authentication gracefully The mapping logic properly transforms database rows to domain objects. </details> <details> <summary>src/profiles/profiles.service.ts (2)</summary> `10-30`: **Clean profile retrieval methods with proper error handling.** Both `findProfileByUsername` and `findProfileByUserId` methods: - Use the repository layer appropriately - Handle not found cases with proper error types - Transform data using domain mappers - Accept optional current user for context The method naming is clear and follows the service layer responsibilities outlined in the architecture. --- `32-71`: **Efficient follow/unfollow implementation with proper validation.** The follow/unfollow methods: - Validate target profile existence first - Check current follow status before making changes - Avoid unnecessary database operations (idempotent) - Return updated profile information - Handle edge cases gracefully This implementation prevents duplicate follows and unnecessary unfollows while maintaining data consistency. </details> <details> <summary>src/profiles/profiles.repository.ts (4)</summary> `10-26`: **Improved profile lookup methods with explicit null handling.** The refactored methods: - Use clear, descriptive method names - Return explicit `Promise<ProfileRow | null>` types - Use array destructuring with nullish coalescing for clean null handling - Include follower relations for complete profile data The `?? null` pattern ensures consistent null returns when no profile is found. --- `28-35`: **Efficient method for retrieving followed user IDs.** The `findFollowedUserIds` method: - Uses selective column projection for performance - Filters correctly by follower ID - Maps results efficiently to return just the IDs This is useful for bulk operations and follow status checks. --- `37-63`: **Robust follow/unfollow operations with proper conflict handling.** Both methods correctly: - Use `onConflictDoNothing()` to handle duplicate follow attempts gracefully - Return boolean success indicators based on affected rows - Use proper query conditions with `and()` for unfollow operations The boolean return values allow the service layer to handle idempotent operations efficiently. --- `65-80`: **Efficient follow relationship check with proper query optimization.** The `findFollowByUsers` method: - Uses selective column projection for minimal data transfer - Applies proper filtering with compound conditions - Uses `limit(1)` for early termination optimization - Returns boolean result for easy consumption This method supports the service layer's validation logic efficiently. </details> </blockquote></details> </details> <!-- This is an auto-generated comment by CodeRabbit for review status -->
…y and change mock user type from UserInDb to UserRow.
… and rename method from getTags to findTags, adjusting expected result format to return an empty array.
…ency and rename method from findByUsername to findProfileByUsername, ensuring clarity in expected behavior.
…ort paths for consistency, enhancing clarity and maintainability across the codebase.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
…entUserId is not provided, ensuring consistent behavior in user following checks.
…t and updatedAt fields, ensuring accurate time tracking and consistency across user-related tables.
…limit values, improving code maintainability and readability.
…rrecting 'deleted' to 'delete' for improved clarity.
…e queries, improving readability and maintainability. Filters for authors, tags, and favorites are now accumulated and applied collectively, streamlining the query construction process.
…edAt fields, ensuring consistency in timestamp handling.
…tion to 'bundler', and add error handling for DrizzleQueryError in the application. Introduced formatDBError function for improved database error reporting.
…aner code organization.
…s to include onUpdate timestamp functionality for improved data integrity.
… and UsersService to use synchronous error throwing for not found scenarios, enhancing clarity and consistency in error handling.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (10)
docs/getting-started.md (1)
49-49: Standardize list marker styleThe final bullet still uses an asterisk. For uniformity, switch this (and the surrounding items) to hyphens to match the rest of the document.
- * If you have any questions or just want to chat, join our [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions). + - If you have any questions or just want to chat, join our [Discord server](https://discord.gg/8UcP9QB5AV) or [GitHub Discussions](https://github.com/bedtime-coders/bedstack/discussions).src/profiles/profiles.service.test.ts (1)
2-3: Import paths updated correctly for layered architecture.The import paths have been properly updated to reflect the new module structure. However, consider removing the
UsersRepositoryimport since it's no longer used in the service instantiation.-import { UsersRepository } from '@/users/users.repository';src/app.module.ts (1)
25-58: Comprehensive error handling implementation with minor improvement suggestions.The error handler effectively covers different error types and provides structured responses. However, consider these improvements:
- The console.error logging (line 51) might not be suitable for production environments
- Consider using a proper logging library instead of console.error
- console.error(error); + // TODO: Replace with proper logging library (e.g., winston, pino) + console.error('[Error Handler]', error);src/common/errors/errors.utils.ts (3)
4-8: Consider adding JSDoc for the temporary type definition.Since this is a temporary type definition, adding documentation about when it can be removed would be helpful.
+/** + * Temporary type definition for Elysia custom status response + * TODO: Remove once Elysia exports this type officially + */ type ElysiaCustomStatusResponse = {
24-29: Consider edge cases in path parsing.The path parsing function handles basic cases well, but consider adding validation for edge cases:
function parsePath(path: string): string { + if (!path || typeof path !== 'string') { + return 'general'; + } return path .replace(/^\//, '') // remove leading slash .replace(/\//g, '.') // convert slash to dot notation .replace(/\[(\d+)\]/g, '.$1'); // optional: convert [0] to .0 }
66-72: Consider improving not found error formatting.The current implementation converts the error message to lowercase, which might not always be appropriate:
export function formatNotFoundError(error: NotFoundError) { return { errors: { - [error.message.toLowerCase()]: 'not found', + [error.message.toLowerCase().replace(/\s+/g, '_')]: 'not found', }, }; }src/tags/tags.service.test.ts (1)
19-24: Consider expanding test coverageWhile the basic test is correct, consider adding more test cases for the tags service:
- Test with actual tag data
- Test error handling scenarios
- Test any filtering or sorting logic
Would you like me to generate additional test cases to improve coverage?
src/articles/articles.schema.ts (2)
9-9: Avoid aliasing pgTable to prevent confusionAliasing
pgTableastablecould be confusing and goes against common Drizzle ORM conventions. Consider usingpgTabledirectly for better clarity and consistency with other schema files.-import { - integer, - primaryKey, - serial, - pgTable as table, - text, - timestamp, -} from 'drizzle-orm/pg-core'; +import { + integer, + primaryKey, + serial, + pgTable, + text, + timestamp, +} from 'drizzle-orm/pg-core';
14-28: Comprehensive articles table definitionThe articles table is well-structured with:
- Unique slug for SEO-friendly URLs
- Proper timestamp management
- Foreign key to users with cascade delete
Consider adding indexes on frequently queried columns like
slugandauthorIdfor better performance.src/articles/articles.repository.ts (1)
249-280: Consider implementing the transaction optimization.The TODO comments correctly identify an optimization opportunity. Currently, these methods perform 1-3 database operations, but could be optimized to a single transaction.
Would you like me to generate a transaction-based implementation for these methods? This would improve both performance and data consistency by ensuring atomicity of the favorite/unfavorite operations.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (23)
GOVERNANCE.md(1 hunks)docs/dev-container.md(2 hunks)docs/getting-started.md(2 hunks)drizzle/migrations/0010_chubby_carnage.sql(1 hunks)drizzle/migrations/meta/0010_snapshot.json(1 hunks)drizzle/migrations/meta/_journal.json(1 hunks)package.json(1 hunks)src/app.module.ts(2 hunks)src/articles/articles.controller.ts(1 hunks)src/articles/articles.repository.ts(3 hunks)src/articles/articles.schema.ts(1 hunks)src/articles/articles.service.test.ts(1 hunks)src/articles/mappers/to-domain.mapper.ts(1 hunks)src/auth/auth.service.test.ts(1 hunks)src/comments/comments.schema.ts(1 hunks)src/comments/comments.service.ts(1 hunks)src/common/errors/errors.utils.ts(1 hunks)src/profiles/profiles.service.test.ts(3 hunks)src/tags/tags.schema.ts(1 hunks)src/tags/tags.service.test.ts(3 hunks)src/users/users.schema.ts(1 hunks)src/users/users.service.test.ts(1 hunks)tsconfig.json(2 hunks)
✅ Files skipped from review due to trivial changes (5)
- src/auth/auth.service.test.ts
- src/articles/articles.service.test.ts
- docs/dev-container.md
- src/users/users.service.test.ts
- drizzle/migrations/meta/0010_snapshot.json
🚧 Files skipped from review as they are similar to previous changes (7)
- drizzle/migrations/meta/_journal.json
- GOVERNANCE.md
- src/articles/articles.controller.ts
- src/comments/comments.schema.ts
- package.json
- src/articles/mappers/to-domain.mapper.ts
- src/comments/comments.service.ts
🧰 Additional context used
🪛 GitHub Check: test (latest)
src/profiles/profiles.service.test.ts
[failure] 30-30: error: expect(received).toThrow(expected)
Expected substring: "Profile not found"
Received message: "profile"
at <anonymous> (/home/runner/work/bedstack/bedstack/src/profiles/profiles.service.test.ts:30:17)
at <anonymous> (/home/runner/work/bedstack/bedstack/src/profiles/profiles.service.test.ts:27:64)
🪛 GitHub Actions: tests
src/profiles/profiles.service.test.ts
[error] 1-1: Test failure: Expected substring 'Profile not found' not found in output.
🪛 LanguageTool
docs/getting-started.md
[style] ~49-~49: Three successive sentences begin with the same word. Consider rewording the sentence or use a thesaurus to find a synonym.
Context: ...oders/bedstack/blob/main/SUPPORT.md). * If you have any questions or just want to ...
(ENGLISH_WORD_REPEAT_BEGINNING_RULE)
🪛 markdownlint-cli2 (0.17.2)
docs/getting-started.md
29-29: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
35-35: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
41-41: Dollar signs used before commands without showing output
null
(MD014, commands-show-output)
46-46: Unordered list style
Expected: dash; Actual: asterisk
(MD004, ul-style)
47-47: Unordered list style
Expected: dash; Actual: asterisk
(MD004, ul-style)
48-48: Unordered list style
Expected: dash; Actual: asterisk
(MD004, ul-style)
49-49: Unordered list style
Expected: dash; Actual: asterisk
(MD004, ul-style)
🔇 Additional comments (28)
tsconfig.json (4)
30-30: Verify compatibility ofmoduleResolution: "bundler"
Switching to the experimental"bundler"resolution is supported only in TS v5+. Ensure your CI and local toolchain use TS >= 5, and validate that existing imports resolve correctly under this strategy.
31-31: ApprovebaseUrlconfiguration
Setting"baseUrl": "./"aligns with your new path alias strategy and simplifies non-relative imports.
34-34: Approve generic path alias
Consolidating specific aliases into"@/*": ["./src/*"]improves maintainability and enforces consistent import paths under the layered architecture.
98-98: ValidatenoUncheckedIndexedAccessimpact
Enabling this option tightens type safety by addingundefinedto indexed access types but may introduce compilation errors. Confirm that the codebase has been updated (e.g., with non-null assertions or runtime guards) and that the full test suite compiles successfully.docs/getting-started.md (2)
11-14: Consistent unordered list styleThe prerequisites list items have been updated to use hyphens, aligning with the project's markdown style. This improves readability and consistency across documentation.
28-31: Normalize fenced code block indentationThe shell command examples are now unindented and aligned to the margin, making them easier to scan and consistent with other docs.
Also applies to: 34-37, 40-43
src/profiles/profiles.service.test.ts (2)
13-15: Method name updated to be more explicit.The method name change from
findByUsernametofindProfileByUsernameimproves clarity and aligns with the domain-focused approach of the layered architecture.
24-24: Service instantiation correctly simplified.The removal of
UsersRepositorydependency from theProfilesServiceconstructor improves separation of concerns and aligns with the layered architecture principles.src/app.module.ts (3)
1-17: LGTM! Clean import organization following layered architecture.The import statements are well-organized and clearly reflect the new layered architecture with dedicated controllers, error utilities, and proper separation of concerns.
83-87: Excellent transition to controller-based architecture.The replacement of plugin-based routing with controller-based routing aligns perfectly with the layered architecture objectives. The controller registration is clean and follows a consistent pattern.
78-78: ```shell
#!/bin/bashCheck if @scalar/api-reference is declared in package.json
grep -R '"@scalar/api-reference"' -n package.json || echo "Not found in package.json"
Locate all uses of scalarVersion in source code
rg "scalarVersion" -n src/
</details> <details> <summary>src/common/errors/errors.utils.ts (2)</summary> `10-17`: **Robust type guard implementation.** The type guard correctly checks for the required properties and handles null/undefined cases properly. --- `31-64`: **Well-structured validation error formatting with good cleanup logic.** The function handles validation errors comprehensively, including: - Fallback for empty error arrays - Path parsing and message extraction - Message cleanup to remove redundant prefixes - Duplicate removal The implementation is solid and handles edge cases well. </details> <details> <summary>src/users/users.schema.ts (4)</summary> `1-10`: **Good modular imports structure** The imports are well-organized, separating domain-specific imports from framework imports. This aligns well with the layered architecture approach. --- `26-31`: **Well-structured user relations** The relations are properly defined with clear naming conventions, establishing connections to followers, following, articles, and favorites. This supports the modular architecture well. --- `33-49`: **Robust follower relationship implementation** The `userFollows` table correctly implements a many-to-many self-referential relationship with: - Composite primary key preventing duplicate follows - Cascade delete maintaining referential integrity - Automatic timestamp management This is a solid implementation of the follower pattern. --- `12-24`: **Consider documenting password hashing strategy** The schema correctly defines the password field, but ensure that password hashing is implemented at the service layer before storing in the database. Consider adding a comment to clarify this security requirement. ```shell #!/bin/bash # Description: Check if password hashing is implemented in the users service or repository # Search for password hashing implementations rg -A 5 "password|hash|bcrypt|argon2" --glob "src/users/**/*.{ts,js}" --glob "!**/*.test.*" --glob "!**/*.spec.*" # Check for hashing libraries in package.json cat package.json | jq '.dependencies | to_entries[] | select(.key | test("bcrypt|argon2|scrypt|crypto"))'src/tags/tags.service.test.ts (2)
1-2: Import path correctly updatedThe Database type import has been properly updated to reflect the new module structure.
12-14: Method name aligned with repository patternThe override method name change from
getTagstofindTagscorrectly aligns with common repository naming conventions.drizzle/migrations/0010_chubby_carnage.sql (1)
1-10: Migration looks correct - verify rollback strategyThe migration properly updates timestamp columns and removes unnecessary defaults. Ensure you have a rollback strategy in case issues arise.
#!/bin/bash # Description: Check for corresponding down migration or rollback strategy # Look for down migration files fd -e sql . drizzle/migrations | grep -E "(down|rollback|revert)" || echo "No down migration files found" # Check if there's a rollback script or documentation fd -e md -e txt . | xargs rg -l "rollback|migration.*revert" || echo "No rollback documentation found"src/tags/tags.schema.ts (3)
26-42: Well-structured article-tag relationshipThe
articleTagsjoin table correctly implements the many-to-many relationship with:
- Composite primary key
- Proper foreign key constraints with cascade delete
- Consistent timestamp management
44-55: Relations properly defined with clear namingThe bidirectional relations between tags and articles are well-defined with descriptive relation names.
11-18: Consider the implications of using tag name as primary keyUsing
nameas the primary key means:
- Tag names cannot be changed without updating all references
- May have performance implications with string-based joins
- Ensures tag uniqueness naturally
This is a valid design choice but ensure it aligns with your business requirements.
#!/bin/bash # Description: Check how tags are used in the codebase to verify if tag names need to be mutable # Search for tag update operations rg -A 5 "update.*tag|tag.*update|rename.*tag|tag.*rename" --glob "src/**/*.{ts,js}" --glob "!**/*.test.*" # Check if there are any tag normalization or validation logic rg -A 3 "tag.*normalize|tag.*validate|tag.*transform" --glob "src/**/*.{ts,js}"src/articles/articles.schema.ts (2)
30-46: Correct implementation of favorites featureThe
favoriteArticlesjoin table properly implements the many-to-many relationship between users and their favorite articles with appropriate constraints.
48-79: Well-organized article relationsThe relations comprehensively cover all article associations:
- Author relationship
- Favorites tracking
- Comments association
- Tags relationship
The relation names are consistent and descriptive.
src/articles/articles.repository.ts (3)
1-24: Well-structured imports and type definitions.The imports follow the new modular architecture pattern nicely, and the
FindFiltersandFindOptionstypes provide clear, type-safe interfaces for the repository methods.
100-149: Filter combination logic is now correctly implemented.Great fix! The previous issue where filters were overwriting each other has been properly resolved. All filters are now accumulated into the
allFiltersarray and combined using logical AND operations before being applied to both queries.
167-247: CRUD methods are well-implemented with proper error handling.The repository methods demonstrate excellent practices:
- Consistent null handling with explicit
?? nullreturns- Proper authorization checks in update/delete operations
- Clear JSDoc documentation
- Type-safe return types
| await expect( | ||
| service.findProfileByUsername(1, 'testuser'), | ||
| ).rejects.toThrow('Profile not found'); |
There was a problem hiding this comment.
🧩 Verification correct
Fix the failing test assertion.
The test is failing because the expected error message doesn't match the actual error thrown by the service. The test expects "Profile not found" but the service throws an error with message "profile".
Verify the actual error message thrown by ProfilesService.findProfileByUsername() when a profile is not found:
Update the test expectation to match the actual error message, or ensure the service throws the expected "Profile not found" message.
🏁 Script executed:
#!/bin/bash
# Description: Find the actual error message thrown by ProfilesService when profile not found
# Expected: Identify the correct error message to use in the test
ast-grep --pattern $'class ProfilesService {
$$$
findProfileByUsername($$$) {
$$$
}
$$$
}'
# Also search for error throwing patterns in the profiles service
rg -A 5 -B 5 "throw.*[Ee]rror|NotFoundError" src/profiles/profiles.service.tsLength of output: 1831
Fix the failing test assertion.
The test is failing because the expected error message doesn't match the actual error thrown by the service. The test expects "Profile not found" but the service throws an error with message "profile".
Verify the actual error message thrown by ProfilesService.findProfileByUsername() when a profile is not found:
#!/bin/bash
# Description: Find the actual error message thrown by ProfilesService when profile not found
# Expected: Identify the correct error message to use in the test
ast-grep --pattern $'class ProfilesService {
$$$
findProfileByUsername($$$) {
$$$
}
$$$
}'
# Also search for error throwing patterns in the profiles service
rg -A 5 -B 5 "throw.*[Ee]rror|NotFoundError" src/profiles/profiles.service.tsUpdate the test expectation to match the actual error message, or ensure the service throws the expected "Profile not found" message.
🧰 Tools
🪛 GitHub Check: test (latest)
[failure] 30-30: error: expect(received).toThrow(expected)
Expected substring: "Profile not found"
Received message: "profile"
at <anonymous> (/home/runner/work/bedstack/bedstack/src/profiles/profiles.service.test.ts:30:17)
at <anonymous> (/home/runner/work/bedstack/bedstack/src/profiles/profiles.service.test.ts:27:64)
🤖 Prompt for AI Agents
In src/profiles/profiles.service.test.ts around lines 28 to 30, the test expects
the error message "Profile not found" but the actual error thrown by
findProfileByUsername() is "profile". Verify the exact error message thrown by
the service by inspecting the service code or running the suggested search
commands. Then update the test assertion to expect the actual error message or
modify the service to throw the expected "Profile not found" message to align
the test with the service behavior.
| export function formatDBError(error: DrizzleQueryError) { | ||
| console.error(error); | ||
| return { | ||
| errors: { | ||
| database: 'error occurred', | ||
| }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Database error handling needs improvement for debugging.
The current implementation logs to console and returns a generic message, which makes debugging difficult in production:
export function formatDBError(error: DrizzleQueryError) {
- console.error(error);
+ // TODO: Replace with proper logging library
+ console.error('[DB Error]', {
+ message: error.message,
+ cause: error.cause,
+ stack: error.stack
+ });
+
return {
errors: {
- database: 'error occurred',
+ database: process.env.NODE_ENV === 'development'
+ ? error.message
+ : 'A database error occurred',
},
};
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export function formatDBError(error: DrizzleQueryError) { | |
| console.error(error); | |
| return { | |
| errors: { | |
| database: 'error occurred', | |
| }, | |
| }; | |
| } | |
| export function formatDBError(error: DrizzleQueryError) { | |
| // TODO: Replace with proper logging library | |
| console.error('[DB Error]', { | |
| message: error.message, | |
| cause: error.cause, | |
| stack: error.stack | |
| }); | |
| return { | |
| errors: { | |
| database: process.env.NODE_ENV === 'development' | |
| ? error.message | |
| : 'A database error occurred', | |
| }, | |
| }; | |
| } |
🤖 Prompt for AI Agents
In src/common/errors/errors.utils.ts around lines 74 to 81, improve the database
error handling by replacing the generic console.error call with structured
logging that captures detailed error information. Modify the returned error
object to include specific error details or codes from the DrizzleQueryError to
aid debugging while avoiding exposing sensitive information. This will enhance
observability and troubleshooting in production environments.
| const resultsCount = await this.db | ||
| .select({ count: count() }) | ||
| .from(resultsQuery); | ||
| .from(countQuery.as('count_query')); |
There was a problem hiding this comment.
Fix count query to avoid duplicate counting.
The count query has a critical bug that can lead to inflated article counts. Since countQuery includes LEFT JOINs to articleTags and favoriteArticles, articles with multiple tags or multiple favorited-by users will create duplicate rows, causing the count to be incorrect.
Apply this fix to get accurate counts:
-const resultsCount = await this.db
- .select({ count: count() })
- .from(countQuery.as('count_query'));
+const resultsCount = await this.db
+ .select({ count: count(sql`distinct ${articles.id}`) })
+ .from(countQuery.as('count_query'));Alternatively, create a simpler count query without the problematic JOINs:
-// Create a separate count query that only includes filtering conditions
-const countQuery = this.db
- .select({ id: articles.id })
- .from(articles)
- .innerJoin(users, eq(users.id, articles.authorId))
- .leftJoin(articleTags, eq(articleTags.articleId, articles.id))
- .leftJoin(favoriteArticles, eq(favoriteArticles.articleId, articles.id));
+// Create a separate count query that only includes filtering conditions
+const countQuery = this.db
+ .select({ id: articles.id })
+ .from(articles)
+ .innerJoin(users, eq(users.id, articles.authorId));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const resultsCount = await this.db | |
| .select({ count: count() }) | |
| .from(resultsQuery); | |
| .from(countQuery.as('count_query')); | |
| const resultsCount = await this.db | |
| - .select({ count: count() }) | |
| + .select({ count: count(sql`distinct ${articles.id}`) }) | |
| .from(countQuery.as('count_query')); |
🤖 Prompt for AI Agents
In src/articles/articles.repository.ts around lines 157 to 159, the count query
uses LEFT JOINs to articleTags and favoriteArticles, which causes duplicate rows
and inflates the article count. To fix this, rewrite the count query to exclude
these JOINs or use a distinct count on the article ID to avoid duplicates.
Ensure the count query only counts unique articles by simplifying the query or
applying DISTINCT on the article identifier.
Description
Refactor the project to Layered Architecture.
For context, see ARCHITECTURE.md and PROJECT_STRUCTURE.md.
For roadmap, see https://github.com/agnyz/bedstack/milestone/3.
PR Checklist
bun docsSummary by CodeRabbit
New Features
Bug Fixes
Refactor
Documentation
Chores