Skip to content

Conversation

@gentamura
Copy link
Member

@gentamura gentamura commented Oct 22, 2025

Summary

  • set up Drizzle configuration in listee-api and generate initial migration synced with @listee/db schema
  • wire up Listee API handler with database-backed dependencies and header authentication
  • expose Next.js route handlers for category and task GET/POST endpoints that proxy to Listee Hono app

Summary by CodeRabbit

  • New Features

    • Added API endpoints for categories, tasks, user-scoped category lists, and health checks.
    • Centralized request dispatch so API routes forward to a single handler.
  • Chores

    • Project initialized with Next.js, TypeScript, package manifest and developer scripts.
    • Database schema, migrations and indexes added.
    • Added ignore rules, formatter/linter/assist configuration and CI workflow.

@coderabbitai
Copy link

coderabbitai bot commented Oct 22, 2025

Walkthrough

Adds project configs and CI, a comprehensive .gitignore, Drizzle ORM config plus migrations and snapshots (categories, profiles, tasks with RLS and indexes), a central Next.js API dispatcher wiring DB/auth/services, and thin Next.js route modules that forward requests to that dispatcher.

Changes

Cohort / File(s) Summary
Ignore & Editor Config
\.gitignore
Adds comprehensive Node.js / Next.js ignore patterns (dependencies, caches, build outputs, env files, OS artifacts, Yarn/PnP artifacts).
Project Tooling & Configs
tsconfig.json, biome.json, next.config.ts, package.json
Adds TypeScript config with Next.js settings and path alias, Biome formatter/linter config, minimal Next.js config export, and root package.json with scripts, dependencies, and devDependencies.
CI Workflow
.github/workflows/ci.yml
Adds GitHub Actions workflow triggering on push and PR, delegating lint/typecheck jobs to reusable workflows.
Drizzle Config
drizzle.config.ts
Adds drizzle-kit config that loads env, validates POSTGRES_URL at runtime, and exports a PostgreSQL config via defineConfig.
Drizzle Migrations
drizzle/0000_gigantic_colleen_wing.sql, drizzle/0001_lying_firebrand.sql, drizzle/0002_gigantic_starbolt.sql
Adds initial schema creating categories, profiles, tasks with defaults, FK constraints, RLS policies; adds index creation statements; adds FK constraint for profiles.default_category_id.
Drizzle Meta / Snapshots & Journal
drizzle/meta/0000_snapshot.json, drizzle/meta/0001_snapshot.json, drizzle/meta/0002_snapshot.json, drizzle/meta/_journal.json
Adds JSON schema snapshots for migration states and updates the journal to include the new migration entry.
Central API Handler
src/app/api/handler.ts
New central dispatch: initializes DB, repositories, services, queries, and auth; exports dispatchToListeeApi(request) which strips an /api prefix, reconstructs the Request, and forwards to the Hono fetch handler.
Route Delegates
src/app/api/categories/[categoryId]/route.ts, src/app/api/categories/[categoryId]/tasks/route.ts, src/app/api/healthz/route.ts, src/app/api/tasks/[taskId]/route.ts, src/app/api/users/[userId]/categories/route.ts
Adds thin Next.js route modules exporting HTTP handlers (GET, POST, PATCH, DELETE, HEAD as applicable) that delegate incoming requests to dispatchToListeeApi.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Client as Client
    participant Route as Next.js Route
    participant Dispatcher as dispatchToListeeApi
    participant Hono as Hono Fetch Handler
    participant Service as Service/Queries
    participant DB as PostgreSQL (RLS)

    Client->>Route: HTTP Request (/api/...)
    Route->>Dispatcher: forward Request
    Dispatcher->>Dispatcher: strip "/api" prefix\nrebuild Request URL
    Dispatcher->>Hono: honoFetchHandler(new Request)
    Hono->>Service: invoke services/queries (with auth)
    Service->>DB: SQL queries/mutations
    DB-->>Service: results (RLS applied)
    Service-->>Hono: Response
    Hono-->>Dispatcher: Response
    Dispatcher-->>Route: Response
    Route-->>Client: HTTP Response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Attention areas:
    • drizzle.config.ts — runtime POSTGRES_URL validation and exported config shape.
    • drizzle/0000_gigantic_colleen_wing.sql — RLS policies, FK cascade/restrict semantics, and unique/index correctness.
    • drizzle/0001_lying_firebrand.sql / drizzle/0002_gigantic_starbolt.sql — index and FK additions.
    • src/app/api/handler.ts — request rewriting (stripApiPrefix), Request reconstruction, and header/auth propagation to honoFetchHandler.
    • Route files — import paths and Next.js handler signatures.

Poem

🐇 I hopped through configs, tidy and bright,
Drizzle seeds planted under moonlight,
Routes whisper "forward" with nimble cheer,
One dispatcher listens — the path is clear,
CI hums softly — carrots near and right.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "Add Drizzle migrations and expose category/task endpoints" directly and accurately reflects the primary changes in the changeset. The title specifically mentions Drizzle migrations (supported by drizzle.config.ts, drizzle SQL migrations, snapshots, and journal files) and exposing category/task endpoints (supported by multiple route handler files for categories, tasks, and users). The title is concise, clear, and uses specific terminology that allows teammates to quickly understand the main deliverables without vague or misleading language. While the PR also includes internal API handler wiring, the title appropriately focuses on the most visible user-facing aspects of the changes.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature-project-api-init

📜 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 5beadaf and c9c6c94.

📒 Files selected for processing (2)
  • .github/workflows/ci.yml (1 hunks)
  • biome.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • biome.json
  • .github/workflows/ci.yml

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.

@gentamura gentamura self-assigned this Oct 23, 2025
@gentamura gentamura closed this Oct 23, 2025
@gentamura gentamura reopened this Oct 23, 2025
@coderabbitai
Copy link

coderabbitai bot commented Oct 23, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

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: 6

🧹 Nitpick comments (7)
package.json (1)

1-33: Consider adding an "engines" field to specify Node.js version requirements.

Adding an engines field helps ensure consistent runtime environments across development and production.

Add an engines field to specify the required Node.js version:

 {
   "name": "listee-api",
   "version": "0.1.0",
   "private": true,
+  "engines": {
+    "node": ">=20.0.0"
+  },
   "scripts": {
tsconfig.json (1)

1-27: LGTM: TypeScript configuration is well-structured for Next.js 15.

The configuration properly enables strict mode, path aliasing, and Next.js plugin support. The setup aligns with Next.js 15 best practices.

Consider updating the target to a more recent ECMAScript version if your deployment environment supports it:

   "compilerOptions": {
-    "target": "ES2017",
+    "target": "ES2022",

ES2022 provides better performance and modern features while maintaining compatibility with Node.js 20+.

.gitignore (1)

34-34: Consider allowing .env.example files to be committed.

The pattern .env* will ignore all environment files including .env.example, which is commonly committed as a template for required environment variables.

Apply this diff to allow example files:

 # env files (can opt-in for committing if needed)
-.env*
+.env
+.env.local
+.env.*.local
+!.env.example

This allows .env.example to be committed while still ignoring actual environment files with secrets.

src/app/api/healthz/route.ts (1)

1-1: Consider using consistent import style across route files.

This file uses a relative import "../handler", while src/app/api/tasks/[taskId]/route.ts uses the path alias "@/app/api/handler". Using the path alias consistently improves maintainability.

Apply this diff for consistency:

-import { dispatchToListeeApi } from "../handler";
+import { dispatchToListeeApi } from "@/app/api/handler";
drizzle.config.ts (1)

1-20: Configuration is sound; consider adding URL format validation.

The Drizzle configuration correctly validates the presence of POSTGRES_URL and uses Next.js environment loading. The structure is appropriate for this use case.

Optionally, consider validating the URL format to catch configuration errors early:

 const databaseUrl = process.env.POSTGRES_URL;

 if (databaseUrl === undefined || databaseUrl.length === 0) {
   throw new Error("POSTGRES_URL is not set.");
 }
+
+try {
+  new URL(databaseUrl);
+} catch {
+  throw new Error("POSTGRES_URL is not a valid URL.");
+}
src/app/api/handler.ts (2)

29-48: Consider resource cleanup strategy for database connections.

The database connection and all dependencies are instantiated at module load, which is a singleton pattern. While this is common in Next.js, consider whether graceful shutdown or connection pooling cleanup is needed.

For production deployments:

  1. Verify that the database driver (@listee/db) handles connection pooling and cleanup automatically
  2. Consider implementing a cleanup handler for graceful shutdowns in containerized environments
  3. Monitor connection pool metrics to ensure connections are properly managed

If explicit cleanup is needed, you may need to expose a shutdown function and hook it into the Next.js server lifecycle.


16-27: Optional: Handle edge cases in stripApiPrefix.

The current implementation works for standard cases but could handle edge cases more defensively.

Consider these enhancements:

 const stripApiPrefix = (pathname: string): string => {
+  // Normalize the pathname to handle double slashes
+  const normalized = pathname.replace(/\/+/g, '/');
+  
-  if (!pathname.startsWith(API_PREFIX)) {
-    return pathname;
+  if (!normalized.startsWith(API_PREFIX)) {
+    return normalized;
   }
 
-  const stripped = pathname.slice(API_PREFIX.length);
+  const stripped = normalized.slice(API_PREFIX.length);
   if (stripped.length === 0) {
     return "/";
   }
 
   return stripped;
 };

This handles cases like //api/categories or /api//categories more robustly.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1d0f964 and 4d63704.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • .gitignore (1 hunks)
  • biome.json (1 hunks)
  • drizzle.config.ts (1 hunks)
  • drizzle/0000_gigantic_colleen_wing.sql (1 hunks)
  • drizzle/meta/0000_snapshot.json (1 hunks)
  • drizzle/meta/_journal.json (1 hunks)
  • next.config.ts (1 hunks)
  • package.json (1 hunks)
  • src/app/api/categories/[categoryId]/route.ts (1 hunks)
  • src/app/api/categories/[categoryId]/tasks/route.ts (1 hunks)
  • src/app/api/handler.ts (1 hunks)
  • src/app/api/healthz/route.ts (1 hunks)
  • src/app/api/tasks/[taskId]/route.ts (1 hunks)
  • src/app/api/users/[userId]/categories/route.ts (1 hunks)
  • tsconfig.json (1 hunks)
🔇 Additional comments (9)
next.config.ts (1)

1-7: LGTM: Minimal configuration is appropriate for initial setup.

The empty Next.js configuration is valid for a new project. The TypeScript-based config file aligns with Next.js 15 best practices.

src/app/api/healthz/route.ts (1)

3-9: LGTM: Health check endpoint properly supports both GET and HEAD methods.

Implementing both GET and HEAD methods for health checks is a best practice, allowing lightweight availability checks via HEAD requests.

biome.json (1)

22-25: The configuration is valid and correct for Biome 2.2.0.

The domains configuration at the linter level is the proper syntax for Biome 2.2.0, where domain values can be "all" (enable all rules), "recommended" (enable only recommended rules), or "none" (disable the domain). Your biome.json configuration matches the official documentation pattern exactly.

Likely an incorrect or invalid review comment.

drizzle/meta/_journal.json (1)

8-8: The review comment is based on incorrect timestamp analysis and should be ignored.

The timestamp 1761059388883 converts to October 21, 2025 at 15:09:48 UTC, which is 2 days in the past (relative to today, October 23, 2025). The original claim that this represents October 22, 2027 is incorrect. There is no system clock issue, no timezone misconfiguration, and no concern about migration ordering—the timestamp is valid and recent.

Likely an incorrect or invalid review comment.

src/app/api/tasks/[taskId]/route.ts (1)

1-5: I need to gather more context about the intended scope of this endpoint before determining if the review comment's concern is valid.

I've gathered information about Next.js 15 API conventions. Now let me search the codebase to understand whether this endpoint needs additional HTTP methods or is intentionally read-only.

Now let me search the codebase for test files or usage patterns to understand if CRUD operations are expected.

Based on my verification, I can now provide the rewritten review comment:

LGTM: Clean delegation pattern for API routing.

The route handler correctly delegates to the centralized dispatchToListeeApi function and follows Next.js 15 app router conventions. By default, unsupported methods return a 405 Method Not Allowed response, so the GET-only implementation is valid. Whether additional HTTP methods (PUT, PATCH, DELETE) should be added depends on your business requirements for this endpoint. Standard REST practice for [taskId] endpoints typically includes update and delete operations, but if this is intentionally read-only, the current implementation is appropriate.

src/app/api/categories/[categoryId]/tasks/route.ts (1)

1-9: LGTM – Clean delegation pattern.

The route handlers correctly delegate to the centralized API handler. This thin routing layer is appropriate for Next.js 15 and maintains consistency with the other route modules in this PR.

src/app/api/users/[userId]/categories/route.ts (1)

1-9: LGTM – Consistent with established pattern.

The implementation correctly follows the centralized dispatch pattern used throughout the API routes.

src/app/api/categories/[categoryId]/route.ts (1)

1-5: LGTM – Follows established routing pattern.

drizzle/0000_gigantic_colleen_wing.sql (1)

49-92: <!-- [scratchpad_update]
[task review_performance_concern_1/1]
Verification confirmed - RLS with EXISTS subqueries IS a known performance issue.

[observations_updated]

  • Web search confirms: "RLS policies in combination with subqueries are unfortunately not each other friends performance-wise" (12-1)
  • Supabase official docs (15-1) explicitly state: "Make sure you've added indexes on any columns used within the Policies which are not already indexed"
  • The review comment's concern about performance is valid and well-documented
  • The recommendation to add indexes IS a best practice per Supabase and industry guidance

[analysis_updated]

  • Review comment is NOT incorrect; it's actually following documented best practices
  • The EXISTS subquery pattern in task policies DOES have performance implications at scale (11-23, 11-24)
  • Adding composite index on categories(id, created_by) would be beneficial for the EXISTS lookups
  • However, the initial script output showed there IS an index on categories but it's not optimally designed for this use case

[finding]

  • The review comment is CORRECT and follows best practices
  • The concern is legitimate: RLS with EXISTS subqueries needs careful index planning
  • Current migration has categories_system_name_idx on (created_by, name) but should have or add support for the specific pattern in the EXISTS query
  • The review comment's suggestions are all appropriate: monitoring, adding better index, denormalization, query analysis

[scratchpad_end] -->

Based on my verification, the review comment is accurate and aligns with documented PostgreSQL and Supabase best practices for RLS performance optimization.

The RLS policies with EXISTS subqueries do present potential performance concerns at scale.

RLS policies in combination with subqueries are unfortunately not each other friends performance-wise. Make sure you've added indexes on any columns used within the Policies which are not already indexed (or primary keys).

The current migration includes categories_system_name_idx on (created_by, name) with a restrictive WHERE clause for system categories only. However, the EXISTS subquery checks both categories.id AND categories.created_by across all categories—a pattern not optimally covered by the existing index. Depending on the way that you construct your RLS policies, performance can vary significantly. In the best case, Postgres can optimize the RLS policy to be as cheap as an additional WHERE clause on your query, or a simple function call before a write. In a much worse case, it can cause a sub-query, per row returned from a database query, which results in queries scaling exponentially.

The suggestions in the review comment—adding an index on categories(id, created_by), denormalization, and query plan analysis—are all legitimate optimization strategies recommended by the PostgreSQL community for this exact pattern.

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

🧹 Nitpick comments (1)
src/app/api/categories/[categoryId]/route.ts (1)

3-13: Implementation follows the established pattern correctly.

The thin proxy pattern is consistent with other routes in the codebase, and the delegation to dispatchToListeeApi works as intended.

Optional: Consider adopting Next.js 15 route handler conventions.

While the current implementation works (because dispatchToListeeApi forwards the full URL), Next.js 15 route handlers with dynamic segments should include the context parameter. This would make the code more idiomatic and future-proof:

-export async function GET(request: Request): Promise<Response> {
+export async function GET(
+  request: Request,
+  context: { params: Promise<{ categoryId: string }> }
+): Promise<Response> {
   return await dispatchToListeeApi(request);
 }

-export async function PATCH(request: Request): Promise<Response> {
+export async function PATCH(
+  request: Request,
+  context: { params: Promise<{ categoryId: string }> }
+): Promise<Response> {
   return await dispatchToListeeApi(request);
 }

-export async function DELETE(request: Request): Promise<Response> {
+export async function DELETE(
+  request: Request,
+  context: { params: Promise<{ categoryId: string }> }
+): Promise<Response> {
   return await dispatchToListeeApi(request);
 }

Note: If you adopt this pattern, consider applying it consistently across all dynamic routes (e.g., tasks/[taskId]/route.ts).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4d63704 and 6603bf4.

📒 Files selected for processing (2)
  • src/app/api/categories/[categoryId]/route.ts (1 hunks)
  • src/app/api/tasks/[taskId]/route.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/api/tasks/[taskId]/route.ts
🧰 Additional context used
🧬 Code graph analysis (1)
src/app/api/categories/[categoryId]/route.ts (4)
src/app/api/tasks/[taskId]/route.ts (3)
  • GET (3-5)
  • PATCH (7-9)
  • DELETE (11-13)
src/app/api/categories/[categoryId]/tasks/route.ts (1)
  • GET (3-5)
src/app/api/users/[userId]/categories/route.ts (1)
  • GET (3-5)
src/app/api/handler.ts (1)
  • dispatchToListeeApi (50-59)
🔇 Additional comments (1)
src/app/api/categories/[categoryId]/route.ts (1)

1-1: LGTM!

The import is correct and follows the project's path alias conventions.

@gentamura gentamura closed this Oct 26, 2025
@gentamura gentamura reopened this Oct 26, 2025
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

🧹 Nitpick comments (1)
.github/workflows/ci.yml (1)

18-18: Consider pinning reusable workflows to a semantic version tag instead of @main.

The workflow references external reusable workflows from listee-ci@main, which means the CI behavior is coupled to the main branch of that repository. This introduces implicit dependencies and potential brittleness if that repository's main branch changes unexpectedly.

For stability and reproducibility, consider pinning to a semantic version tag (e.g., @v1.0.0) or a commit SHA instead:

- lint:
-   uses: listee-dev/listee-ci/.github/workflows/lint.yml@main
+ lint:
+   uses: listee-dev/listee-ci/.github/workflows/lint.yml@v1.0.0

If @main is intentional for staying up-to-date with the latest workflows, document this decision.

Also applies to: 21-21, 24-24

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b7979b0 and 4888062.

📒 Files selected for processing (1)
  • .github/workflows/ci.yml (1 hunks)
🔇 Additional comments (1)
.github/workflows/ci.yml (1)

11-14: Permissions configuration looks appropriate.

The explicit permissions (contents, pull-requests, id-token with write access) align with common CI use cases for code updates, PR commenting, and OIDC workflows.

@gentamura gentamura force-pushed the feature-project-api-init branch from bd94836 to 7ae7bdc Compare October 26, 2025 11:27
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bd94836 and 7ae7bdc.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (2)
  • .github/workflows/ci.yml (1 hunks)
  • package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • package.json
🧰 Additional context used
🪛 Checkov (3.2.334)
.github/workflows/ci.yml

[medium] 17-18: Basic Auth Credentials

(CKV_SECRET_4)

🔇 Additional comments (3)
.github/workflows/ci.yml (3)

3-9: LGTM! Workflow triggers are appropriately configured to run on main pushes and all PR branches.


11-14: LGTM! Permissions are appropriately scoped for the workflow's needs.


20-28: LGTM! The reusable workflow pattern is clean and follows the DRY principle, delegating lint, typecheck, and test responsibilities to centralized CI workflows.

@gentamura gentamura force-pushed the feature-project-api-init branch from 5beadaf to f29ad6c Compare October 26, 2025 12:26
@gentamura gentamura force-pushed the feature-project-api-init branch from f29ad6c to c9c6c94 Compare October 26, 2025 12:29
@gentamura gentamura merged commit f6f15ac into main Oct 26, 2025
3 checks passed
@gentamura gentamura deleted the feature-project-api-init branch October 26, 2025 12:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants