Skip to content

Add create APIs for categories and tasks#9

Merged
gentamura merged 5 commits intomainfrom
feature-add-create-apis
Oct 22, 2025
Merged

Add create APIs for categories and tasks#9
gentamura merged 5 commits intomainfrom
feature-add-create-apis

Conversation

@gentamura
Copy link
Member

@gentamura gentamura commented Oct 20, 2025

Summary by CodeRabbit

  • New Features

    • Added endpoints to create categories and to create tasks within categories; both require authentication, enforce ownership, and validate payloads. Task creation verifies the target category exists and returns clear error responses for missing/invalid resources.
  • Tests

    • Expanded fixtures and test coverage to exercise category and task creation flows, including endpoint-level integration scenarios.

@coderabbitai
Copy link

coderabbitai bot commented Oct 20, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Types
packages/types/src/api.ts
Added CreateCategoryParams, CreateTaskParams, repository param types; added create method signatures to CategoryQueries, TaskQueries, CategoryRepository, TaskRepository, CategoryService, TaskService; extended RegisterTaskRoutesOptions and AppDependencies with categoryQueries.
Repository Layer
packages/api/src/repositories/category-repository.ts, packages/api/src/repositories/task-repository.ts
Added create methods that INSERT records and return created rows; introduced Create*RepositoryParams imports; error handling when insertion yields no row.
Service Layer
packages/api/src/services/category-service.ts, packages/api/src/services/task-service.ts
Added create methods delegating to repositories; task service validates category existence and throws "Category not found" when missing.
Query Layer
packages/api/src/queries/category-queries.ts, packages/api/src/queries/task-queries.ts
Exposed create methods that delegate to service layer; imported Create*Params types.
Routes
packages/api/src/routes/categories.ts, packages/api/src/routes/tasks.ts
Added POST /users/:userId/categories and POST /categories/:categoryId/tasks routes with auth, user-scope checks, JSON parsing, payload validation, category existence check (via categoryQueries), and error mapping (400/401/403/404/500).
Validation Utilities
packages/api/src/utils/validation.ts
Added isRecord, isNonEmptyString, and isBoolean type-guard utilities used by route payload parsers.
App Boot & Tests
packages/api/src/app.ts, packages/api/src/app.test.ts
Forwarded categoryQueries into task route registration; test fixtures extended with createCategoryQueries/createTaskQueries, nextCategoryId/nextTaskId counters, and helpers to create categories/tasks; added tests for POST creation endpoints.
DB Index Import
packages/db/src/index.ts
Added fileURLToPath import from node:url — no behavior change.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰 I nibble at code and plant a seed,
New categories, tasks — sprout at speed,
Routes with checks, validations true,
From query to DB — fresh things grew! 🌱

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Add create APIs for categories and tasks" directly and accurately reflects the main changes in the changeset. The PR implements comprehensive creation functionality across multiple layers: API routes (POST /users/:userId/categories and POST /categories/:categoryId/tasks), service layers (CategoryService.create and TaskService.create), repository layers (CategoryRepository.create and TaskRepository.create), and query layers (CategoryQueries.create and TaskQueries.create). The title is specific and clear about what is being added and which entities are affected, making it immediately understandable to someone reviewing the commit history.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-add-create-apis

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c52727d and 877a15a.

📒 Files selected for processing (1)
  • packages/db/src/index.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • packages/db/src/index.ts

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

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 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: Prefer typeof for boolean checks.

Using literal comparison works but typeof is 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 null for 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 categoryQueries is 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:

  1. Remove this route-level check and rely solely on the service layer validation
  2. Make category validation mandatory by requiring categoryQueries in the route setup

This 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

📥 Commits

Reviewing files that changed from the base of the PR and between 882fd0b and 1e72009.

📒 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.ts
  • packages/api/src/services/task-service.ts
  • packages/api/src/queries/category-queries.ts
  • packages/api/src/routes/categories.ts
  • packages/api/src/routes/tasks.ts
  • packages/api/src/utils/validation.ts
  • packages/api/src/repositories/category-repository.ts
  • packages/api/src/app.ts
  • packages/types/src/api.ts
  • packages/api/src/app.test.ts
  • packages/api/src/repositories/task-repository.ts
  • packages/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.ts
  • packages/api/src/services/task-service.ts
  • packages/api/src/queries/category-queries.ts
  • packages/api/src/routes/categories.ts
  • packages/api/src/routes/tasks.ts
  • packages/api/src/repositories/category-repository.ts
  • packages/api/src/repositories/task-repository.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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.ts
  • packages/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 ?? null conversion (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 userId for both createdBy and updatedBy is 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) and CreateTaskRepositoryParams.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 categoryQueries to task routes enables category existence validation during task creation, which aligns with the service layer's validation logic in task-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 userId for both createdBy and updatedBy is 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 categoryQueries from 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 updatedBy from createdBy when not specified is consistent with creation semantics.

@gentamura gentamura self-assigned this Oct 20, 2025
@gentamura gentamura merged commit aad9e2c into main Oct 22, 2025
4 checks passed
@gentamura gentamura deleted the feature-add-create-apis branch October 22, 2025 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant