Add create APIs for categories and tasks#9
Conversation
WalkthroughAdds create operations for categories and tasks across tests, queries, services, repositories, routes, and types; introduces payload validation helpers and request validation; forwards categoryQueries into task route registration; adds POST endpoints for creating categories and tasks with error mapping and tests exercising creation flows. Changes
Sequence Diagram(s)%%{init: {"themeVariables":{"sequenceActorFill":"#E8F7E7","signalColor":"#7AA2F7","actorBorder":"#7A7A7A"}} }%%
sequenceDiagram
actor Client
participant Routes as Routes
participant Q as Queries
participant S as Services
participant R as Repositories
participant DB as Database
Client->>Routes: POST /users/:userId/categories (name, kind)
Routes->>Routes: auth & user-scope check
Routes->>Routes: parse & validate payload
Routes->>Q: Q.create({ userId, name, kind })
Q->>S: S.create(params)
S->>R: R.create(params)
R->>DB: INSERT category
DB-->>R: row
R-->>S: Category
S-->>Q: Category
Q-->>Routes: 201 + Category
Routes-->>Client: 201 + Category
rect rgba(122,162,247,0.06)
Client->>Routes: POST /categories/:categoryId/tasks (name, description, isChecked)
Routes->>Routes: auth check
Routes->>Q: categoryQueries.findById(categoryId, userId)
alt category exists
Routes->>Routes: parse & validate task payload
Routes->>Q: Q.create({ categoryId, userId, ...payload })
Q->>S: S.create(params)
S->>R: R.create(params)
R->>DB: INSERT task
DB-->>R: row
R-->>S: Task
S-->>Q: Task
Q-->>Routes: 201 + Task
Routes-->>Client: 201 + Task
else category missing
Q-->>Routes: null
Routes-->>Client: 404
end
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches✅ Passed checks (3 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/api/src/app.test.ts (1)
1-1: Fix formatting issue.The pipeline indicates that the file content differs from the expected formatting output. Please run the formatter to resolve this.
🧹 Nitpick comments (4)
packages/api/src/utils/validation.ts (2)
1-3: Consider explicitly rejecting arrays.The current implementation allows arrays to pass since
typeof [] === "object". While this is safe (arrays would fail subsequent property validation), being more explicit improves clarity.Apply this diff for stricter validation:
export function isRecord(value: unknown): value is Record<string, unknown> { - return typeof value === "object" && value !== null; + return typeof value === "object" && value !== null && !Array.isArray(value); }
13-15: Prefertypeoffor boolean checks.Using literal comparison works but
typeofis more idiomatic for type guards.Apply this diff:
export function isBoolean(value: unknown): value is boolean { - return value === true || value === false; + return typeof value === "boolean"; }packages/api/src/routes/tasks.ts (2)
25-58: Consider normalizing empty descriptions to null.The validation correctly trims the description, but empty strings are preserved rather than converted to null. If an empty string
""is provided, it remains""after trimming.Consider whether empty or whitespace-only descriptions should be normalized to
nullfor consistency:const description = - typeof descriptionValue === "string" ? descriptionValue.trim() : null; + typeof descriptionValue === "string" + ? descriptionValue.trim() || null + : null;This would ensure that empty strings are treated the same as absent descriptions.
139-148: Consider removing optional category validation from the route.The route performs an optional category existence check only when
categoryQueriesis provided. However, this validation is duplicated by the service layer (caught at lines 173-175 as "Category not found").As per coding guidelines, routes should "delegate exclusively to queries and handle HTTP translation/status; avoid business logic in routes." The category existence check could be considered business logic and should be handled consistently in the service/query layer rather than optionally in the route.
Consider either:
- Remove this route-level check and rely solely on the service layer validation
- Make category validation mandatory by requiring
categoryQueriesin the route setupThis would simplify the route and maintain clearer separation of concerns.
Based on coding guidelines
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (12)
packages/api/src/app.test.ts(12 hunks)packages/api/src/app.ts(1 hunks)packages/api/src/queries/category-queries.ts(2 hunks)packages/api/src/queries/task-queries.ts(2 hunks)packages/api/src/repositories/category-repository.ts(2 hunks)packages/api/src/repositories/task-repository.ts(2 hunks)packages/api/src/routes/categories.ts(3 hunks)packages/api/src/routes/tasks.ts(4 hunks)packages/api/src/services/category-service.ts(2 hunks)packages/api/src/services/task-service.ts(2 hunks)packages/api/src/utils/validation.ts(1 hunks)packages/types/src/api.ts(5 hunks)
🧰 Additional context used
📓 Path-based instructions (7)
packages/*/src/**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
packages/*/src/**/*.{ts,tsx}: Avoid implicit any; TypeScript is run with strict enabled
Prefer type guards or the satisfies operator over as casts where appropriate
Prefer unknown for external inputs
Use PascalCase for types and enums
Use camelCase for variables and functions
Ensure source comments are written in English
Files:
packages/api/src/queries/task-queries.tspackages/api/src/services/task-service.tspackages/api/src/queries/category-queries.tspackages/api/src/routes/categories.tspackages/api/src/routes/tasks.tspackages/api/src/utils/validation.tspackages/api/src/repositories/category-repository.tspackages/api/src/app.tspackages/types/src/api.tspackages/api/src/app.test.tspackages/api/src/repositories/task-repository.tspackages/api/src/services/category-service.ts
packages/*/src/{routes,queries,services,repositories}/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Maintain dependency direction routes → queries → services → repositories
Files:
packages/api/src/queries/task-queries.tspackages/api/src/services/task-service.tspackages/api/src/queries/category-queries.tspackages/api/src/routes/categories.tspackages/api/src/routes/tasks.tspackages/api/src/repositories/category-repository.tspackages/api/src/repositories/task-repository.tspackages/api/src/services/category-service.ts
packages/*/src/queries/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Queries should compose services/repositories per use case and inject dependencies via factories
Files:
packages/api/src/queries/task-queries.tspackages/api/src/queries/category-queries.ts
packages/*/src/services/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Services may depend on repositories, but keep service layer thin and extract complex domain logic into dedicated modules
Files:
packages/api/src/services/task-service.tspackages/api/src/services/category-service.ts
packages/*/src/routes/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Routes must delegate exclusively to queries and handle HTTP translation/status; avoid business logic in routes
Files:
packages/api/src/routes/categories.tspackages/api/src/routes/tasks.ts
packages/*/src/repositories/**/*.ts
📄 CodeRabbit inference engine (AGENTS.md)
Repositories must not depend on services and should encapsulate external access, returning plain or domain-specific types
Files:
packages/api/src/repositories/category-repository.tspackages/api/src/repositories/task-repository.ts
packages/*/src/**/{*.test.ts,__tests__/**/*.ts}
📄 CodeRabbit inference engine (AGENTS.md)
packages/*/src/**/{*.test.ts,__tests__/**/*.ts}: Write test names and descriptions in English
Co-locate tests as *.test.ts or under tests/ using Bun’s test runner
Files:
packages/api/src/app.test.ts
🧬 Code graph analysis (10)
packages/api/src/queries/task-queries.ts (1)
packages/types/src/api.ts (1)
CreateTaskParams(45-51)
packages/api/src/services/task-service.ts (2)
packages/types/src/api.ts (1)
CreateTaskParams(45-51)packages/db/src/schema/index.ts (1)
Task(157-157)
packages/api/src/queries/category-queries.ts (1)
packages/types/src/api.ts (1)
CreateCategoryParams(23-27)
packages/api/src/routes/categories.ts (3)
packages/api/src/utils/validation.ts (2)
isRecord(1-3)isNonEmptyString(5-11)packages/api/src/routes/auth-utils.ts (1)
tryAuthenticate(7-20)packages/api/src/utils/error.ts (1)
toErrorMessage(1-21)
packages/api/src/routes/tasks.ts (3)
packages/api/src/utils/validation.ts (3)
isRecord(1-3)isNonEmptyString(5-11)isBoolean(13-15)packages/api/src/routes/auth-utils.ts (1)
tryAuthenticate(7-20)packages/api/src/utils/error.ts (1)
toErrorMessage(1-21)
packages/api/src/repositories/category-repository.ts (2)
packages/types/src/api.ts (1)
CreateCategoryRepositoryParams(71-76)packages/db/src/schema/index.ts (2)
Category(100-100)categories(57-98)
packages/types/src/api.ts (1)
packages/types/src/db.ts (1)
PaginatedResult(22-26)
packages/api/src/app.test.ts (3)
packages/api/src/queries/category-queries.ts (1)
createCategoryQueries(9-42)packages/api/src/queries/task-queries.ts (1)
createTaskQueries(9-41)packages/types/src/api.ts (2)
CategoryQueries(17-21)TaskQueries(39-43)
packages/api/src/repositories/task-repository.ts (2)
packages/types/src/api.ts (1)
CreateTaskRepositoryParams(108-115)packages/db/src/schema/index.ts (2)
Task(157-157)tasks(102-155)
packages/api/src/services/category-service.ts (1)
packages/types/src/api.ts (1)
CreateCategoryParams(23-27)
🪛 GitHub Actions: CI
packages/api/src/app.test.ts
[error] 1-1: File content differs from formatting output.
🔇 Additional comments (20)
packages/api/src/repositories/task-repository.ts (1)
48-67: LGTM! Clean repository implementation.The create method follows established patterns and correctly maps parameters to the database schema. The
description ?? nullconversion (line 53) properly handles the optional field, and error handling is consistent with the repository layer's responsibility.packages/api/src/services/task-service.ts (1)
25-45: LGTM! Proper validation and defaults.The implementation correctly validates category existence when the repository is available (lines 26-35) and applies sensible defaults for optional fields (lines 40-41). Using
userIdfor bothcreatedByandupdatedByis appropriate for creation operations.packages/types/src/api.ts (1)
20-20: LGTM! Comprehensive and consistent type definitions.The type hierarchy correctly reflects the create flow across all layers. The distinction between
CreateTaskParams.isChecked(optional) andCreateTaskRepositoryParams.isChecked(required) properly captures the service layer's defaulting behavior. All interfaces follow TypeScript and coding conventions with readonly modifiers and proper casing.Also applies to: 23-27, 42-42, 45-51, 71-76, 83-83, 91-91, 108-115, 120-120, 126-126, 131-131, 150-150
packages/api/src/queries/category-queries.ts (1)
29-35: LGTM! Clean query delegation.The create method appropriately delegates to the service layer, maintaining the established dependency direction and pattern consistency with other query methods.
packages/api/src/app.ts (1)
18-18: LGTM! Enables cross-entity validation.Forwarding
categoryQueriesto task routes enables category existence validation during task creation, which aligns with the service layer's validation logic intask-service.ts.packages/api/src/routes/categories.ts (1)
152-186: LGTM! Secure route with proper validation.The route correctly implements authentication (lines 153-156), authorization (lines 158-161), and input validation (lines 170-173). The JSON parsing is safely wrapped in try-catch (lines 163-168), and the creation flow properly delegates to queries.
One minor consideration: all creation errors currently return 500 (line 184), which includes potential validation errors from the service layer. This is acceptable for the current implementation but could be refined later to distinguish validation failures (400) from server errors (500).
packages/api/src/queries/task-queries.ts (1)
26-34: LGTM! Proper query layer delegation.The create method correctly delegates to the service layer, maintaining separation of concerns and following the established pattern of other query methods in this file.
packages/api/src/repositories/category-repository.ts (2)
7-7: LGTM!The import addition is necessary for the new create method.
151-170: LGTM!The create method implementation is correct and follows the repository pattern. It properly maps parameters to database columns, uses
.returning()to retrieve the inserted row, and includes defensive error handling.Also applies to: 175-175
packages/api/src/services/category-service.ts (2)
5-5: LGTM!The import addition is necessary for the new create method signature.
26-33: LGTM!The create method correctly delegates to the repository layer with proper parameter mapping. Using
userIdfor bothcreatedByandupdatedByis appropriate for entity creation.Also applies to: 38-38
packages/api/src/routes/tasks.ts (3)
3-4: LGTM!The utility imports are appropriate for the new validation and error handling logic.
90-90: LGTM!Extracting
categoryQueriesfrom options is appropriate for the new category validation logic.
150-179: LGTM!The request handling properly validates JSON parsing, payload structure, and includes comprehensive error handling with appropriate HTTP status codes (400 for validation, 404 for not found, 201 for created, 500 for server errors).
packages/api/src/app.test.ts (6)
63-63: LGTM!The fixture creators now return both the queries and the underlying data arrays, enabling tests to inspect and validate the test data. This is a useful pattern for testing.
Also applies to: 82-82, 98-98, 115-115, 152-152, 170-170, 187-187, 238-238
127-147: LGTM!The test properly validates the category creation endpoint, checking both the response status (201) and the returned data.
199-233: LGTM!The test properly validates the task creation endpoint with appropriate setup of both task and category queries. It correctly verifies the created task is associated with the right category.
255-255: LGTM!The test fixture create implementation correctly generates unique IDs and adds new categories to the front of the array (appropriate for descending createdAt sorting).
Also applies to: 279-291
299-299: LGTM!The test fixture create implementation correctly generates unique IDs, handles optional fields with appropriate defaults, and appends new tasks to the array.
Also applies to: 306-306, 315-334
340-390: LGTM!The test helper functions are now more flexible, supporting optional parameters with sensible defaults. The pattern of deriving
updatedByfromcreatedBywhen not specified is consistent with creation semantics.
Summary by CodeRabbit
New Features
Tests