Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented Oct 27, 2025

Summary by CodeRabbit

  • Breaking Changes

    • Removed @omit and @password attributes from schema support.
  • New Features

    • Added a JSON:API–style REST handler with rich filtering, sorting, pagination, includes, relationship CRUD, and export points.
    • Added a helper to format schema validation (Zod) errors.
  • Bug Fixes

    • Improved JSON handling for PostgreSQL and more stable relation ordering behavior.
  • Chores

    • Updated package dependencies and helper exports.
  • Tests

    • Added REST handler tests for adapters and extended ORM orderBy tests.

Copilot AI review requested due to automatic review settings October 27, 2025 00:18
@coderabbitai
Copy link

coderabbitai bot commented Oct 27, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Consolidated utility imports/exports, documented removal of @omit/@password, added a JSON:API RestApiHandler and Zod error formatter, changed ORM order-by to generate join aliases for relation ordering, adjusted Postgres JSON primitive handling, updated server package dependencies, and adapted tests to the new REST handler.

Changes

Cohort / File(s) Summary
Breaking Notes
BREAKINGCHANGES.md
Documented removal of the @omit and @password attributes.
Common helpers exports & imports
packages/common-helpers/src/index.ts, packages/common-helpers/src/clone.ts, packages/common-helpers/src/enumerable.ts
Added re-exports (clone, enumerable); clone.ts now imports isPlainObject from a local ./is-plain-object module.
ORM order-by aliasing
packages/orm/src/client/crud/dialects/base-dialect.ts
Order-by processing now uses enumerate(orderBy) to get indices and generates join aliases (modelAlias$orderBy$<index>) for to-one relation left joins; recursive order building receives the alias as the model reference.
ORM utility import updates
packages/orm/src/client/crud/operations/base.ts, packages/orm/src/client/crud/validator/index.ts
Replaced local utility imports with centralized @zenstackhq/common-helpers imports (clone, enumerate, invariant).
Postgres JSON handling
packages/orm/src/client/crud/dialects/postgresql.ts
Added Json handling branch in transformPrimitive for non-array Json values; strings, numbers, booleans are stringified for Postgres emission; objects/arrays returned as-is.
REST API handler & exports
packages/server/src/api/rest/index.ts, packages/server/src/api/index.ts
Added RestApiHandler and RestApiHandlerOptions (JSON:API-style REST CRUD, routing, serialization, relationships) and re-exported handler and option types from api/index.ts.
RPC errors & Zod helper
packages/server/src/api/rpc/index.ts, packages/server/src/api/utils.ts
makeGenericErrorResponse now handles Error instances specially and logs stacks; added exported getZodErrorMessage(error: ZodError): string using zod-validation-error v3/v4 detection.
Server package deps
packages/server/package.json
Added runtime deps: ts-japi, url-pattern, zod-validation-error; devDeps: supertest, zod; added peerDep: zod.
Adapter tests -> REST handler
packages/server/test/adapter/express.test.ts, packages/server/test/adapter/next.test.ts
Updated adapter tests to instantiate and use RestApiHandler({ schema, endpoint }); added tests for middleware interaction and REST CRUD flows.
E2E tests
tests/e2e/orm/client-api/find.test.ts
Added tests verifying findFirst supports duplicate orderBy fields, including to-one relation ordering cases.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Files needing focused review:
    • packages/server/src/api/rest/index.ts — large, dense handler with routing, serialization, relationship and error mapping logic.
    • packages/orm/src/client/crud/dialects/base-dialect.ts — join alias generation and recursive order-building changes for relation ordering.
    • packages/orm/src/client/crud/dialects/postgresql.ts — Postgres JSON emission edge cases.
    • Cross-package re-exports/imports (packages/common-helpers/* and ORM modules) — watch for import source changes and potential circulars.
    • Adapter tests — ensure middleware/sendResponse semantics and endpoint wiring are correct.

Possibly related PRs

Poem

🐰 I hopped through code with careful paws,

Aliases stitched where joins find cause,
Helpers gathered, exports tidy and neat,
REST handlers listen on every street,
Tests applaud — a carrot-shaped feat! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.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 "feat(server): migrate rest api handler" directly aligns with the primary feature being introduced in this changeset. The most substantial changes include the new RestApiHandler implementation in packages/server/src/api/rest/index.ts with comprehensive JSON:API support, new public exports in packages/server/src/api/index.ts, updated test files demonstrating REST API usage, and new dependencies for supporting REST functionality. While the changeset also includes supporting refactorings in packages/common-helpers (consolidating imports/exports) and packages/orm (refactoring orderBy processing and enumerate usage), these are foundational changes that enable the REST API handler rather than parallel features. The title is concise, specific to the server package and REST handler feature, and clearly communicates the primary change.
✨ 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 feat/rest-handler

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6717713 and e538c09.

📒 Files selected for processing (1)
  • packages/orm/src/client/crud/dialects/postgresql.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/orm/src/client/crud/dialects/postgresql.ts
🔇 Additional comments (1)
packages/orm/src/client/crud/dialects/postgresql.ts (1)

61-68: ****

The current implementation is actually correct and properly addresses the original escaping issue. The distinction between stringifying primitives and passing objects as-is is intentional and correct:

  • Arrays (lines 42-50): Stringified when passed to non-array Json fields due to a known node-pg limitation (issue #374)
  • Primitives (lines 61-68): Stringified because they're "simple JSON values" that require wrapping for PostgreSQL
  • Objects/null (line 66): Passed as-is because node-pg handles them correctly internally

The test suite confirms this approach works: Json: { foo: 'bar' } test cases pass without pre-stringification, proving objects don't need to be stringified. The past review's suggestion to stringify ALL non-array values would cause double-serialization and break object handling. The current implementation is superior to the originally suggested approach.

Likely an incorrect or invalid review comment.


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.

@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @ymc9's task —— View job


Code Review: REST API Handler Migration ✅

This is a substantial and well-implemented migration that adds comprehensive REST API functionality to ZenStack's server package.

Overall Assessment: APPROVED

Strengths:

  • Comprehensive Implementation: Full JSON:API compliance with proper resource/relationship/collection patterns
  • Security: Robust input validation with Zod schemas, proper error handling for policy violations
  • Performance: Smart pagination, efficient queries with count aggregation, selective relationship loading
  • Test Coverage: Extensive test suite (~3,152 lines) covering edge cases and error conditions
  • Architecture: Clean separation of concerns, proper URL pattern matching, well-structured serializers

Detailed Analysis:

✅ Code Quality & Best Practices

  • Well-structured class with clear method separation
  • Proper use of TypeScript with comprehensive type definitions
  • Consistent error handling patterns following JSON:API standards
  • Good use of design patterns (URL pattern matching, serializer pattern)

✅ Security Considerations

  • All payloads validated through strict Zod schemas (createUpdatePayloadSchema, relationship schemas)
  • Proper URL decoding with decodeURIComponent
  • ZenStack policy integration properly handles authorization (RejectedByPolicyError)
  • No injection vulnerabilities detected in query building

✅ Performance Considerations

  • Configurable pagination (default 100, customizable, can disable)
  • Efficient query construction with skip/take for offset-based pagination
  • Smart relationship loading based on include parameters
  • Proper use of _count for totals without over-fetching data

✅ Test Coverage

  • Comprehensive CRUD operation testing
  • Edge case coverage (invalid IDs, missing resources, relationship errors)
  • Multiple schema types tested (compound IDs, different field types)
  • Both positive and negative test scenarios

Minor Areas for Future Enhancement:

🔍 Type Safety Improvements

  • Several as any casts could be more specific (lines 380, 488, 520, etc.)
  • Consider stronger typing for client method calls: (client as any)[type]

📝 TODOs to Address

  • Line 407: TODO: PUT for full update - Consider implementing for complete REST compliance
  • Lines 1469, 1517: Custom @@id name support for compound IDs
  • Line 1882: Inequality filters for more advanced querying

🏗️ Breaking Changes
The documented breaking changes are reasonable for a major version:

  • Policy error behavior changes are well-documented
  • Removed deprecated attributes (@omit, @password) is appropriate cleanup

Dependencies Analysis:

  • ts-japi: Solid choice for JSON:API compliance
  • url-pattern: Appropriate for flexible URL matching
  • superjson: Good for complex data serialization
  • All dependencies are well-established and properly used

Recommendation: MERGE 🚀

This is a high-quality implementation that significantly expands ZenStack's API capabilities. The code follows best practices, has excellent test coverage, and properly handles security concerns. The few minor improvements noted above can be addressed in future iterations.


Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR migrates the REST API handler to a new implementation architecture, aligning it with the RPC handler pattern and updating the test suite accordingly.

Key Changes:

  • Introduced a new RestApiHandler class with improved structure and error handling
  • Migrated tests from commented-out code to active test cases
  • Added support for duplicate sort fields in ORM queries
  • Refactored utility functions and dependencies

Reviewed Changes

Copilot reviewed 14 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
packages/server/src/api/rest/index.ts New REST API handler implementation with JSON:API compliance
packages/server/src/api/index.ts Updated exports to include REST API handler and options
packages/server/src/api/utils.ts Added Zod error formatting utility function
packages/server/src/api/rpc/index.ts Enhanced error handling and added documentation
packages/server/test/adapter/next.test.ts Migrated REST handler tests from comments to active tests
packages/server/test/adapter/express.test.ts Activated and updated REST handler tests for Express adapter
packages/server/package.json Added dependencies for REST API functionality
tests/e2e/orm/client-api/find.test.ts Added test coverage for duplicate sort fields
packages/orm/src/client/crud/operations/base.ts Updated imports to use common-helpers utilities
packages/orm/src/client/crud/validator/index.ts Updated imports to use common-helpers utilities
packages/orm/src/client/crud/dialects/base-dialect.ts Fixed duplicate join handling in order by operations
packages/common-helpers/src/index.ts Exported clone and enumerate utilities
packages/common-helpers/src/clone.ts Fixed import path for isPlainObject
BREAKINGCHANGES.md Added breaking change note about removed attributes
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

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

🧹 Nitpick comments (10)
BREAKINGCHANGES.md (1)

4-4: Add brief migration guidance for removed attributes.

Document how users should replace @omit and @password (e.g., schema alternatives, access policy patterns, or code mods), and note the first release version/date where removal occurs to ease upgrades.

packages/server/src/api/utils.ts (1)

4-6: Consider avoiding dual static imports to reduce bundle size.

Importing both v3 and v4 subpaths can pull in extra code. If your peer zod is pinned to v3 (see package.json), you can import only the matching subpath, or do a lazy await import() with a guarded fallback.

Confirm whether your bundler tree-shakes the unused subpath when both are statically imported.

packages/server/src/api/rpc/index.ts (1)

183-189: Good: clearer generic error, plus debug stack.

Returning err.message improves client debuggability; stack is kept to logs only.

Optional hardening: gate the client-facing message behind a flag (e.g., exposeInternalErrors?: boolean) and default to a generic text in production.

If desired, I can draft a small diff adding this option across RPCApiHandlerOptions and makeGenericErrorResponse.

packages/orm/src/client/crud/dialects/base-dialect.ts (2)

757-757: Avoid shadowing and improve readability.

The callback parameter shadows the outer orderBy. Consider renaming to orderByItem.

- enumerate(orderBy).forEach((orderBy, index) => {
+ enumerate(orderBy).forEach((orderByItem, index) => {
-    for (const [field, value] of Object.entries<any>(orderBy)) {
+    for (const [field, value] of Object.entries<any>(orderByItem)) {

843-853: Unique join aliases fix collision; consider de-dup for perf.

Using ${modelAlias}$orderBy$${index} prevents alias clashes and is correct. If callers often order by multiple fields on the same to‑one relation, you can group fields per relation to reuse a single join and reduce redundant joins.

No behavior change required; just a potential optimization.

packages/server/src/api/rest/index.ts (5)

261-278: Auto-include idDivider in URL segment charset.

Docs say “it should be included”, but this foot-guns easily. Append it when missing.

Apply:

         this.idDivider = options.idDivider ?? DEFAULT_ID_DIVIDER;
-        const segmentCharset = options.urlSegmentCharset ?? 'a-zA-Z0-9-_~ %';
+        const rawSegmentCharset = options.urlSegmentCharset ?? 'a-zA-Z0-9-_~ %';
+        const segmentCharset = rawSegmentCharset.includes(this.idDivider)
+            ? rawSegmentCharset
+            : rawSegmentCharset + this.idDivider;

1171-1178: Consider 204 No Content for DELETE.

JSON:API allows 204 for delete; returning an empty body reduces payload.

Apply:

-        return {
-            status: 200,
-            body: { meta: {} },
-        };
+        return { status: 204, body: null };

826-835: Paginator “first” link should include page[offset]=0.

Improves client predictability.

Apply:

-            first: this.replaceURLSearchParams(baseUrl, { 'page[limit]': limit }),
+            first: this.replaceURLSearchParams(baseUrl, { 'page[offset]': 0, 'page[limit]': limit }),

1543-1551: Avoid selecting IDs for all relationships unconditionally.

includeRelationshipIds eagerly selects every relation’s IDs, increasing query cost even when not serialized. Consider:

  • Only include IDs for relations that appear in include/fields or for relationship endpoints.
  • Gate behind an option (e.g., eagerRelationshipIds?: boolean, default false).

I can propose a scoped refactor to compute a targeted relation set from include/fields[*].

Also applies to: 714-716, 493-504


2003-2025: Minor: enrich error payloads with instance and log correlation ID.

For operability, include a request-scoped ID and optionally an instance pointer to the offending URL.

If you want, I can wire a requestId through RequestContext and add it here.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62d731d and 6c836f6.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (14)
  • BREAKINGCHANGES.md (1 hunks)
  • packages/common-helpers/src/clone.ts (1 hunks)
  • packages/common-helpers/src/index.ts (1 hunks)
  • packages/orm/src/client/crud/dialects/base-dialect.ts (3 hunks)
  • packages/orm/src/client/crud/operations/base.ts (1 hunks)
  • packages/orm/src/client/crud/validator/index.ts (1 hunks)
  • packages/server/package.json (2 hunks)
  • packages/server/src/api/index.ts (1 hunks)
  • packages/server/src/api/rest/index.ts (1 hunks)
  • packages/server/src/api/rpc/index.ts (2 hunks)
  • packages/server/src/api/utils.ts (2 hunks)
  • packages/server/test/adapter/express.test.ts (2 hunks)
  • packages/server/test/adapter/next.test.ts (2 hunks)
  • tests/e2e/orm/client-api/find.test.ts (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/server/src/api/rpc/index.ts
  • tests/e2e/orm/client-api/find.test.ts
  • packages/orm/src/client/crud/validator/index.ts
  • packages/common-helpers/src/clone.ts
  • packages/common-helpers/src/index.ts
  • packages/server/src/api/utils.ts
  • packages/server/src/api/index.ts
  • packages/server/package.json
  • packages/server/src/api/rest/index.ts
  • packages/orm/src/client/crud/operations/base.ts
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/server/test/adapter/next.test.ts
  • packages/server/test/adapter/express.test.ts
tests/e2e/**

📄 CodeRabbit inference engine (CLAUDE.md)

End-to-end tests must live under tests/e2e/

Files:

  • tests/e2e/orm/client-api/find.test.ts
🧠 Learnings (1)
📚 Learning: 2025-10-21T16:04:56.292Z
Learnt from: ymc9
PR: zenstackhq/zenstack-v3#319
File: packages/runtime/src/client/crud/dialects/base-dialect.ts:745-747
Timestamp: 2025-10-21T16:04:56.292Z
Learning: In packages/runtime/src/client/crud/dialects/base-dialect.ts, it's intentional that buildCursorFilter applies default ordering (via makeDefaultOrderBy fallback) while buildOrderBy does not. This ensures cursor-based pagination always has stable ordering for correctness, while regular queries remain unordered unless explicitly specified. This design is to be consistent with Prisma's pagination requirements.

Applied to files:

  • packages/orm/src/client/crud/dialects/base-dialect.ts
🧬 Code graph analysis (5)
packages/server/src/api/rpc/index.ts (2)
packages/server/src/api/utils.ts (1)
  • log (9-27)
packages/common-helpers/src/safe-json-stringify.ts (1)
  • safeJSONStringify (4-12)
packages/server/src/api/rest/index.ts (9)
packages/sdk/src/schema/schema.ts (3)
  • SchemaDef (10-18)
  • FieldDef (60-75)
  • ModelDef (20-37)
packages/server/src/types.ts (4)
  • LogConfig (17-17)
  • ApiHandler (67-77)
  • RequestContext (22-47)
  • Response (52-62)
packages/server/src/api/utils.ts (3)
  • registerCustomSerializers (32-53)
  • getZodErrorMessage (58-64)
  • log (9-27)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
packages/orm/src/client/errors.ts (5)
  • ZenStackError (4-4)
  • InputValidationError (9-17)
  • RejectedByPolicyError (68-76)
  • NotFoundError (36-43)
  • QueryError (22-26)
packages/orm/src/client/contract.ts (1)
  • ClientContract (50-170)
packages/common-helpers/src/enumerable.ts (1)
  • enumerate (9-17)
packages/common-helpers/src/clone.ts (1)
  • clone (6-24)
packages/common-helpers/src/param-case.ts (1)
  • paramCase (5-22)
packages/orm/src/client/crud/dialects/base-dialect.ts (2)
packages/common-helpers/src/enumerable.ts (1)
  • enumerate (9-17)
packages/orm/src/client/query-utils.ts (1)
  • buildJoinPairs (193-211)
packages/server/test/adapter/next.test.ts (2)
packages/testtools/src/client.ts (1)
  • createTestClient (52-169)
packages/server/src/api/rest/index.ts (1)
  • RestApiHandler (124-2036)
packages/server/test/adapter/express.test.ts (3)
packages/testtools/src/client.ts (1)
  • createPolicyTestClient (179-190)
packages/server/src/api/rest/index.ts (2)
  • schema (283-285)
  • RestApiHandler (124-2036)
packages/server/test/utils.ts (2)
  • schema (3-29)
  • makeUrl (31-33)
🔇 Additional comments (19)
packages/server/src/api/rpc/index.ts (1)

20-29: Docs addition looks good.

Option descriptions improve discoverability.

packages/server/package.json (2)

58-62: Deps align with new REST surface.

ts-japi, url-pattern, and zod-validation-error additions make sense.


73-76: Dev deps look appropriate for tests.

supertest and zod inclusion matches added handlers.

packages/orm/src/client/crud/dialects/base-dialect.ts (2)

1-1: Consolidate to shared helpers — good move.

Switching to @zenstackhq/common-helpers for enumerate/utils improves consistency across modules.


205-220: enumerate for composite filters reads well.

AND/OR now gracefully handles null/one/many payloads; NOT via not(AND(...)) matches expected semantics.

Based on learnings

packages/server/src/api/utils.ts (1)

58-64: The current implementation correctly follows Zod's documented approach—no changes needed.

The '_zod' in error check is actually the supported and recommended method for detecting Zod v3 vs v4 at runtime, as documented by the Zod library for cross-version support. The property is stable and reliable: Zod v4 adds _zod to error objects, while Zod v3 lacks it entirely. This is a deliberate breaking change that distinguishes the two versions.

The try/catch fallback approach is unnecessary and adds runtime overhead for a check that is already well-defined by the library. The current code correctly:

  • Imports fromError from both v3 and v4 subpaths (confirmed by web search)
  • Uses the stable, documented property check to discriminate versions
  • Calls the appropriate formatter without coupling beyond what's necessary
packages/orm/src/client/crud/validator/index.ts (1)

1-1: LGTM! Clean utility consolidation.

The import has been successfully refactored to use the centralized @zenstackhq/common-helpers package, combining both enumerate and invariant imports. This aligns with the PR's objective to consolidate utility functions across packages.

packages/orm/src/client/crud/operations/base.ts (1)

2-2: LGTM! Utility imports properly consolidated.

The import successfully consolidates clone and enumerate utilities with the existing invariant and isPlainObject imports from @zenstackhq/common-helpers. This refactoring aligns with the PR's centralization strategy.

packages/common-helpers/src/index.ts (1)

1-2: LGTM! Public API properly expanded.

The addition of clone and enumerable exports successfully centralizes these utilities, enabling other packages (like @zenstackhq/orm) to import them from a single location. This is the key enabler for the consolidation changes throughout the PR.

packages/common-helpers/src/clone.ts (1)

1-1: LGTM! Proper local import avoids circular dependency.

Changing the isPlainObject import from the external package reference to a local relative import (./is-plain-object) is the correct approach within the same package, avoiding potential circular dependency issues while maintaining the same functionality.

tests/e2e/orm/client-api/find.test.ts (2)

73-81: Good test coverage for duplicate orderBy fields.

The test correctly verifies that duplicate sort fields in orderBy are handled properly. This validates the improved alias handling for order-by relationships mentioned in the PR objectives.


176-180: Good test coverage for duplicate orderBy with relations.

The test validates that duplicate sort fields work correctly with to-one relations, ensuring the improved order-by alias handling extends to relationship queries.

packages/server/src/api/index.ts (1)

1-2: LGTM! Clean public API surface.

The barrel exports properly expose both RestApiHandler and RPCApiHandler along with their respective option types, providing a clean and consistent public API for the server package.

packages/server/test/adapter/next.test.ts (2)

8-8: LGTM! Import updated for REST handler.

The import correctly includes RestApiHandler to support the new REST API test coverage.


235-291: Excellent REST API handler test coverage.

The test comprehensively validates the new RestApiHandler with the Next.js adapter, including:

  • JSON:API format request/response handling
  • CRUD operations (create, read, update, delete)
  • Query filtering with URL parameters
  • Proper HTTP status codes

The test structure follows existing patterns and provides good coverage of the REST API functionality.

packages/server/test/adapter/express.test.ts (3)

8-8: LGTM! Import added for REST handler tests.

The import correctly includes RestApiHandler from the REST API module to support the new test coverage.


95-148: Comprehensive REST API handler test coverage.

The test thoroughly validates the RestApiHandler with the Express adapter, covering:

  • JSON:API formatted requests and responses
  • Collection and single resource endpoints
  • Query filtering with URL parameters
  • Full CRUD operations
  • Proper HTTP status codes and response structure

The test follows Express testing best practices and aligns well with the existing RPC handler tests.


150-174: Excellent coverage of custom middleware integration.

This test validates the sendResponse: false option, ensuring that downstream middleware can access the REST API response via res.locals['zenstack']. This is important for custom error handling and response transformation scenarios.

packages/server/src/api/rest/index.ts (1)

459-461: Use invalidVerb for unsupported HTTP methods.

Current default branch returns invalidPath.
[ suggest_recommended_refactor ]
Apply:

-                default:
-                    return this.makeError('invalidPath');
+                default:
+                    return this.makeError('invalidVerb');

@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @ymc9's task —— View job


Code Review: REST API Handler Migration

I'm conducting a comprehensive code review of this PR focusing on code quality, potential bugs, performance, security, and test coverage.

Review Tasks:

  • Analyze RestApiHandler implementation for bugs and security issues
  • Review code quality and best practices
  • Examine performance considerations
  • Assess test coverage and quality
  • Check utility function migrations
  • Review dependency changes and breaking changes
  • Provide overall assessment and recommendations

Detailed Analysis

🔍 Security Analysis

✅ Strengths:

  • Comprehensive input validation using Zod schemas for all payloads
  • Proper URL decoding with decodeURIComponent prevents injection issues
  • ZenStack policy integration properly handles authorization (RejectedByPolicyError)
  • No obvious SQL injection vulnerabilities in query building

🚨 Critical Issues Found:

  1. BigInt/Decimal Precision Loss (packages/server/src/api/rest/index.ts:1614)

    • parseInt used for BigInt loses precision for large numbers
    • parseFloat for Decimal loses precision
    • Fix: Use BigInt(value) and return string for Decimal to preserve precision
  2. Incorrect ID Mapping Logic (packages/server/src/api/rest/index.ts:1232)

    • typeof info === 'string' check never matches - single unique fields are objects
    • Breaks ID resolution for external ID mappings
    • Fix: Check info.type === 'string' or use entry key name
  3. Wrong Error Status for Unknown Errors (packages/server/src/api/rest/index.ts:207)

    • Unknown errors return 400 instead of 500
    • Fix: Change status to 500 for server errors

🏗️ Code Quality & Best Practices

✅ Strengths:

  • Well-structured class with clear method separation
  • Comprehensive type definitions with TypeScript
  • Consistent error handling patterns following JSON:API standards
  • Good use of design patterns (URL pattern matching, serializer pattern)

⚠️ Areas for Improvement:

  1. Type Safety: Multiple as any casts throughout the code (lines 380, 488, 520, etc.)
  2. URL Segment Charset: Should auto-include idDivider in charset to prevent foot-guns
  3. Filter Handling: Missing explicit handling for ZodError in catch blocks
  4. Unique Join Aliases: Good fix for order-by collisions using ${modelAlias}$orderBy$${index}

🚀 Performance Considerations

✅ Good Practices:

  • Configurable pagination (default 100, customizable, can disable)
  • Efficient query construction with skip/take for offset-based pagination
  • Smart relationship loading based on include parameters
  • Proper use of _count for totals without over-fetching

⚠️ Potential Optimizations:

  1. Eager Relationship ID Selection: includeRelationshipIds selects ALL relation IDs even when not needed
  2. Pagination Link Generation: Could optimize by only including relevant query params
  3. Filter Processing: Complex nested filter logic could be optimized for common cases

🧪 Test Coverage Assessment

✅ Excellent Coverage:

  • Comprehensive test suite (~3,167 lines in rest.test.ts)
  • Tests cover CRUD operations, edge cases, error conditions
  • Multiple schema types tested (compound IDs, different field types)
  • Both positive and negative test scenarios
  • Integration tests with Express and Next.js adapters

🔄 Utility Function Migration

✅ Clean Migration:

  • Successfully consolidates utilities to @zenstackhq/common-helpers
  • Proper import path updates across packages
  • Fixes circular dependency issue in clone.ts
  • Enhanced order-by processing with unique join aliases

📦 Dependencies & Breaking Changes

✅ Appropriate Additions:

  • ts-japi: Good choice for JSON:API compliance
  • url-pattern: Appropriate for flexible URL matching
  • zod-validation-error: Proper error formatting

⚠️ Issue:

  • Zod peer dependency too restrictive (~3.25.0 should be ^3.25.0 || ^4.0.0)

✅ Breaking Changes Well Documented:

  • Removal of @omit and @password attributes is appropriate cleanup
  • Policy error behavior changes are documented

🎯 Overall Assessment: APPROVED WITH CRITICAL FIXES NEEDED

This is a substantial and well-implemented feature that adds comprehensive REST API functionality to ZenStack. The code follows good architectural principles and has excellent test coverage.

Critical Issues Requiring Fix:

  1. BigInt/Decimal precision handling - Could cause data corruption
  2. ID mapping logic for external IDs - Breaks functionality
  3. Error status codes - Incorrect client vs server error classification

Recommended Actions:

  1. Fix the 3 critical issues identified above
  2. Consider widening Zod peer dependency range
  3. Address type safety improvements in future iterations
  4. Optimize relationship ID selection for better performance

The implementation demonstrates solid understanding of REST API best practices, JSON:API compliance, and security considerations. Once the critical issues are addressed, this will be a valuable addition to the ZenStack ecosystem.


Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
packages/server/src/api/rest/index.ts (1)

1214-1236: Fix single-unique field resolution in externalIdMapping.

When externalIdName matches a single-unique constraint, line 1227 incorrectly uses info.type (the field's type like 'String') instead of the field name. For single-unique entries in uniqueFields, the iteration key name is the field name.

Apply this diff:

         for (const [name, info] of Object.entries(modelDef.uniqueFields)) {
             if (name === externalIdName) {
                 if (typeof info.type === 'string') {
                     // single unique field
-                    return [this.requireField(model, info.type)];
+                    return [this.requireField(model, name)];
                 } else {
                     // compound unique fields
                     return Object.keys(info).map((f) => this.requireField(model, f));
                 }
             }
         }
🧹 Nitpick comments (2)
packages/server/src/api/rest/index.ts (2)

1-120: Consider deferring module-level side effect.

registerCustomSerializers() is called at module load (line 120). This is typically safe but can cause issues if the module is imported multiple times or in specific initialization orders.

Consider moving the registration to the constructor or a lazy-init pattern if module load side-effects become problematic.


1404-1446: Clone operation may be expensive for large result sets.

clone(items) at line 1404 creates a deep copy of the entire result set to inject compound IDs. This could impact performance with large datasets.

Consider mutating in place if memory/performance becomes a concern, or document the trade-off.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6c836f6 and d01f101.

📒 Files selected for processing (2)
  • packages/server/package.json (2 hunks)
  • packages/server/src/api/rest/index.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/package.json
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/server/src/api/rest/index.ts
🧬 Code graph analysis (1)
packages/server/src/api/rest/index.ts (9)
packages/sdk/src/schema/schema.ts (3)
  • SchemaDef (10-18)
  • FieldDef (60-75)
  • ModelDef (20-37)
packages/server/src/types.ts (4)
  • LogConfig (17-17)
  • ApiHandler (67-77)
  • RequestContext (22-47)
  • Response (52-62)
packages/server/src/api/utils.ts (3)
  • registerCustomSerializers (32-53)
  • getZodErrorMessage (58-64)
  • log (9-27)
packages/common-helpers/src/lower-case-first.ts (1)
  • lowerCaseFirst (1-3)
packages/orm/src/client/errors.ts (5)
  • ZenStackError (4-4)
  • InputValidationError (9-17)
  • RejectedByPolicyError (68-76)
  • NotFoundError (36-43)
  • QueryError (22-26)
packages/orm/src/client/contract.ts (1)
  • ClientContract (50-170)
packages/common-helpers/src/enumerable.ts (1)
  • enumerate (9-17)
packages/common-helpers/src/clone.ts (1)
  • clone (6-24)
packages/common-helpers/src/param-case.ts (1)
  • paramCase (5-22)
🔇 Additional comments (4)
packages/server/src/api/rest/index.ts (4)

125-282: Well-structured REST API handler implementation.

The class architecture is clean with proper separation between routing, validation, CRUD operations, and serialization. The constructor properly initializes all required metadata and mappings.


334-472: Request routing and error handling are well organized.

The method dispatch by HTTP verb and URL pattern matching provides clear routing logic. Error handling properly delegates to specialized handlers.


1579-1625: Type coercion properly handles precision-sensitive types.

BigInt and Decimal coercion correctly use their respective constructors to preserve precision, avoiding the pitfalls of parseInt/parseFloat.


1679-1781: Filter building correctly resolves nested type ID fields.

The filter builder properly computes idFields per iteration using currType.name (line 1738), ensuring nested filters use the correct model's ID fields.

@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @ymc9's task —— View job


Code Review: REST API Handler Migration ✅

I'm conducting a comprehensive code review of this PR focusing on code quality, potential bugs, performance, security, and test coverage.

Review Tasks:

  • Analyze RestApiHandler implementation for bugs and security issues
  • Review code quality and best practices
  • Examine performance considerations
  • Assess test coverage and quality
  • Check utility function migrations
  • Review dependency changes and breaking changes
  • Provide overall assessment and recommendations

Detailed Analysis

🔍 Security Analysis

✅ Strengths:

  • Comprehensive input validation using Zod schemas for all payloads
  • Proper URL decoding with decodeURIComponent prevents injection issues
  • ZenStack policy integration properly handles authorization (RejectedByPolicyError)
  • No obvious SQL injection vulnerabilities in query building

🚨 Critical Issues Found:

  1. BigInt/Decimal Precision Loss (packages/server/src/api/rest/index.ts:1614)

    • parseInt used for BigInt loses precision for large numbers
    • parseFloat for Decimal loses precision
    • Fix: Use BigInt(value) and return string for Decimal to preserve precision
  2. Incorrect ID Mapping Logic (packages/server/src/api/rest/index.ts:1232)

    • typeof info === 'string' check never matches - single unique fields are objects
    • Breaks ID resolution for external ID mappings
    • Fix: Check info.type === 'string' or use entry key name
  3. Wrong Error Status for Unknown Errors (packages/server/src/api/rest/index.ts:207)

    • Unknown errors return 400 instead of 500
    • Fix: Change status to 500 for server errors

🏗️ Code Quality & Best Practices

✅ Strengths:

  • Well-structured class with clear method separation
  • Comprehensive type definitions with TypeScript
  • Consistent error handling patterns following JSON:API standards
  • Good use of design patterns (URL pattern matching, serializer pattern)

⚠️ Areas for Improvement:

  1. Type Safety: Multiple as any casts throughout the code (lines 380, 488, 520, etc.)
  2. URL Segment Charset: Should auto-include idDivider in charset to prevent foot-guns
  3. Filter Handling: Missing explicit handling for ZodError in catch blocks
  4. Unique Join Aliases: Good fix for order-by collisions using ${modelAlias}$orderBy$${index}

🚀 Performance Considerations

✅ Good Practices:

  • Configurable pagination (default 100, customizable, can disable)
  • Efficient query construction with skip/take for offset-based pagination
  • Smart relationship loading based on include parameters
  • Proper use of _count for totals without over-fetching

⚠️ Potential Optimizations:

  1. Eager Relationship ID Selection: includeRelationshipIds selects ALL relation IDs even when not needed
  2. Pagination Link Generation: Could optimize by only including relevant query params
  3. Filter Processing: Complex nested filter logic could be optimized for common cases

🧪 Test Coverage Assessment

✅ Excellent Coverage:

  • Comprehensive test suite (~3,167 lines in rest.test.ts)
  • Tests cover CRUD operations, edge cases, error conditions
  • Multiple schema types tested (compound IDs, different field types)
  • Both positive and negative test scenarios
  • Integration tests with Express and Next.js adapters

🔄 Utility Function Migration

✅ Clean Migration:

  • Successfully consolidates utilities to @zenstackhq/common-helpers
  • Proper import path updates across packages
  • Fixes circular dependency issue in clone.ts
  • Enhanced order-by processing with unique join aliases

📦 Dependencies & Breaking Changes

✅ Appropriate Additions:

  • ts-japi: Good choice for JSON:API compliance
  • url-pattern: Appropriate for flexible URL matching
  • zod-validation-error: Proper error formatting

⚠️ Issue:

  • Zod peer dependency too restrictive (~3.25.0 should be ^3.25.0 || ^4.0.0)

✅ Breaking Changes Well Documented:

  • Removal of @omit and @password attributes is appropriate cleanup
  • Policy error behavior changes are documented

🎯 Overall Assessment: APPROVED WITH CRITICAL FIXES NEEDED

This is a substantial and well-implemented feature that adds comprehensive REST API functionality to ZenStack. The code follows good architectural principles and has excellent test coverage.

Critical Issues Requiring Fix:

  1. BigInt/Decimal precision handling - Could cause data corruption
  2. ID mapping logic for external IDs - Breaks functionality
  3. Error status codes - Incorrect client vs server error classification

Recommended Actions:

  1. Fix the 3 critical issues identified above
  2. Consider widening Zod peer dependency range
  3. Address type safety improvements in future iterations
  4. Optimize relationship ID selection for better performance

The implementation demonstrates solid understanding of REST API best practices, JSON:API compliance, and security considerations. Once the critical issues are addressed, this will be a valuable addition to the ZenStack ecosystem.


@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @ymc9's task —— View job


Code Review: REST API Handler Migration ✅

I'm conducting a comprehensive code review of this PR focusing on code quality, potential bugs, performance, security, and test coverage.

Review Tasks:

  • Analyze RestApiHandler implementation for bugs and security issues
  • Review code quality and best practices
  • Examine performance considerations
  • Assess test coverage and quality
  • Check utility function migrations
  • Review dependency changes and breaking changes
  • Provide overall assessment and recommendations

Detailed Analysis

🔍 Security Analysis

✅ Strengths:

  • Comprehensive input validation using Zod schemas for all payloads
  • Proper URL decoding with decodeURIComponent prevents injection issues
  • ZenStack policy integration properly handles authorization (RejectedByPolicyError)
  • No obvious SQL injection vulnerabilities in query building

🚨 Critical Issues Found:

  1. BigInt/Decimal Precision Loss (packages/server/src/api/rest/index.ts:1614)

    • parseInt used for BigInt loses precision for large numbers
    • parseFloat for Decimal loses precision
    • Fix: Use BigInt(value) and return string for Decimal to preserve precision
  2. Incorrect ID Mapping Logic (packages/server/src/api/rest/index.ts:1232)

    • typeof info === 'string' check never matches - single unique fields are objects
    • Breaks ID resolution for external ID mappings
    • Fix: Check info.type === 'string' or use entry key name
  3. Wrong Error Status for Unknown Errors (packages/server/src/api/rest/index.ts:207)

    • Unknown errors return 400 instead of 500
    • Fix: Change status to 500 for server errors

🏗️ Code Quality & Best Practices

✅ Strengths:

  • Well-structured class with clear method separation
  • Comprehensive type definitions with TypeScript
  • Consistent error handling patterns following JSON:API standards
  • Good use of design patterns (URL pattern matching, serializer pattern)

⚠️ Areas for Improvement:

  1. Type Safety: Multiple as any casts throughout the code (lines 380, 488, 520, etc.)
  2. URL Segment Charset: Should auto-include idDivider in charset to prevent foot-guns
  3. Filter Handling: Missing explicit handling for ZodError in catch blocks
  4. Unique Join Aliases: Good fix for order-by collisions using ${modelAlias}$orderBy$${index}

🚀 Performance Considerations

✅ Good Practices:

  • Configurable pagination (default 100, customizable, can disable)
  • Efficient query construction with skip/take for offset-based pagination
  • Smart relationship loading based on include parameters
  • Proper use of _count for totals without over-fetching

⚠️ Potential Optimizations:

  1. Eager Relationship ID Selection: includeRelationshipIds selects ALL relation IDs even when not needed
  2. Pagination Link Generation: Could optimize by only including relevant query params
  3. Filter Processing: Complex nested filter logic could be optimized for common cases

🧪 Test Coverage Assessment

✅ Excellent Coverage:

  • Comprehensive test suite (~3,167 lines in rest.test.ts)
  • Tests cover CRUD operations, edge cases, error conditions
  • Multiple schema types tested (compound IDs, different field types)
  • Both positive and negative test scenarios
  • Integration tests with Express and Next.js adapters

🔄 Utility Function Migration

✅ Clean Migration:

  • Successfully consolidates utilities to @zenstackhq/common-helpers
  • Proper import path updates across packages
  • Fixes circular dependency issue in clone.ts
  • Enhanced order-by processing with unique join aliases

📦 Dependencies & Breaking Changes

✅ Appropriate Additions:

  • ts-japi: Good choice for JSON:API compliance
  • url-pattern: Appropriate for flexible URL matching
  • zod-validation-error: Proper error formatting

⚠️ Issue:

  • Zod peer dependency too restrictive (~3.25.0 should be ^3.25.0 || ^4.0.0)

✅ Breaking Changes Well Documented:

  • Removal of @omit and @password attributes is appropriate cleanup
  • Policy error behavior changes are documented

🎯 Overall Assessment: APPROVED WITH CRITICAL FIXES NEEDED

This is a substantial and well-implemented feature that adds comprehensive REST API functionality to ZenStack. The code follows good architectural principles and has excellent test coverage.

Critical Issues Requiring Fix:

  1. BigInt/Decimal precision handling - Could cause data corruption
  2. ID mapping logic for external IDs - Breaks functionality
  3. Error status codes - Incorrect client vs server error classification

Recommended Actions:

  1. Fix the 3 critical issues identified above
  2. Consider widening Zod peer dependency range
  3. Address type safety improvements in future iterations
  4. Optimize relationship ID selection for better performance

The implementation demonstrates solid understanding of REST API best practices, JSON:API compliance, and security considerations. Once the critical issues are addressed, this will be a valuable addition to the ZenStack ecosystem.


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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d01f101 and 6717713.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • packages/orm/src/client/crud/dialects/postgresql.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
{packages,samples,tests}/**

📄 CodeRabbit inference engine (CLAUDE.md)

Place packages only under packages/, samples/, or tests/

Files:

  • packages/orm/src/client/crud/dialects/postgresql.ts

@claude
Copy link

claude bot commented Oct 27, 2025

Claude finished @ymc9's task —— View job


Code Review: REST API Handler Migration

I'm conducting a comprehensive code review of this PR focusing on code quality, potential bugs, performance, security, and test coverage.

Review Tasks:

  • Analyze RestApiHandler implementation for bugs and security issues
  • Review code quality and best practices
  • Examine performance considerations
  • Assess test coverage and quality
  • Check utility function migrations
  • Review dependency changes and breaking changes
  • Provide overall assessment and recommendations

Detailed Analysis

🔍 Security Analysis

✅ Strengths:

  • Comprehensive input validation using Zod schemas for all payloads
  • Proper URL decoding with decodeURIComponent prevents injection issues
  • ZenStack policy integration properly handles authorization (RejectedByPolicyError)
  • No obvious SQL injection vulnerabilities in query building

🚨 Critical Issues Found:

  1. BigInt/Decimal Precision Loss (packages/server/src/api/rest/index.ts:1614)

    • parseInt used for BigInt loses precision for large numbers
    • parseFloat for Decimal loses precision
    • Fix: Use BigInt(value) and return string for Decimal to preserve precision
  2. Incorrect ID Mapping Logic (packages/server/src/api/rest/index.ts:1232)

    • typeof info === 'string' check never matches - single unique fields are objects
    • Breaks ID resolution for external ID mappings
    • Fix: Check info.type === 'string' or use entry key name
  3. Wrong Error Status for Unknown Errors (packages/server/src/api/rest/index.ts:207)

    • Unknown errors return 400 instead of 500
    • Fix: Change status to 500 for server errors

🏗️ Code Quality & Best Practices

✅ Strengths:

  • Well-structured class with clear method separation
  • Comprehensive type definitions with TypeScript
  • Consistent error handling patterns following JSON:API standards
  • Good use of design patterns (URL pattern matching, serializer pattern)

⚠️ Areas for Improvement:

  1. Type Safety: Multiple as any casts throughout the code (lines 380, 488, 520, etc.)
  2. URL Segment Charset: Should auto-include idDivider in charset to prevent foot-guns
  3. Filter Handling: Missing explicit handling for ZodError in catch blocks
  4. Unique Join Aliases: Good fix for order-by collisions using ${modelAlias}$orderBy$${index}

🚀 Performance Considerations

✅ Good Practices:

  • Configurable pagination (default 100, customizable, can disable)
  • Efficient query construction with skip/take for offset-based pagination
  • Smart relationship loading based on include parameters
  • Proper use of _count for totals without over-fetching

⚠️ Potential Optimizations:

  1. Eager Relationship ID Selection: includeRelationshipIds selects ALL relation IDs even when not needed
  2. Pagination Link Generation: Could optimize by only including relevant query params
  3. Filter Processing: Complex nested filter logic could be optimized for common cases

🧪 Test Coverage Assessment

✅ Excellent Coverage:

  • Comprehensive test suite (~3,167 lines in rest.test.ts)
  • Tests cover CRUD operations, edge cases, error conditions
  • Multiple schema types tested (compound IDs, different field types)
  • Both positive and negative test scenarios
  • Integration tests with Express and Next.js adapters

🔄 Utility Function Migration

✅ Clean Migration:

  • Successfully consolidates utilities to @zenstackhq/common-helpers
  • Proper import path updates across packages
  • Fixes circular dependency issue in clone.ts
  • Enhanced order-by processing with unique join aliases

📦 Dependencies & Breaking Changes

✅ Appropriate Additions:

  • ts-japi: Good choice for JSON:API compliance
  • url-pattern: Appropriate for flexible URL matching
  • zod-validation-error: Proper error formatting

⚠️ Issue:

  • Zod peer dependency too restrictive (~3.25.0 should be ^3.25.0 || ^4.0.0)

✅ Breaking Changes Well Documented:

  • Removal of @omit and @password attributes is appropriate cleanup
  • Policy error behavior changes are documented

🎯 Overall Assessment: APPROVED WITH CRITICAL FIXES NEEDED

This is a substantial and well-implemented feature that adds comprehensive REST API functionality to ZenStack. The code follows good architectural principles and has excellent test coverage.

Critical Issues Requiring Fix:

  1. BigInt/Decimal precision handling - Could cause data corruption
  2. ID mapping logic for external IDs - Breaks functionality
  3. Error status codes - Incorrect client vs server error classification

Recommended Actions:

  1. Fix the 3 critical issues identified above
  2. Consider widening Zod peer dependency range
  3. Address type safety improvements in future iterations
  4. Optimize relationship ID selection for better performance

The implementation demonstrates solid understanding of REST API best practices, JSON:API compliance, and security considerations. Once the critical issues are addressed, this will be a valuable addition to the ZenStack ecosystem.


@ymc9 ymc9 merged commit 95da8d2 into dev Oct 27, 2025
5 checks passed
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