Skip to content

Conversation

@ymc9
Copy link
Member

@ymc9 ymc9 commented May 26, 2025

Summary by CodeRabbit

  • New Features

    • Added support for the "upsert" operation, allowing users to create or update records in a single call.
    • Introduced the ability to omit specific fields from results in all CRUD operations, including create, update, delete, and upsert.
  • Bug Fixes

    • Improved handling of field selection and omission to ensure mutually exclusive options for select, include, and omit.
  • Tests

    • Added comprehensive tests for the new upsert functionality and omit option in create operations.
  • Documentation

    • Updated task status in the project documentation to reflect completion of the upsert feature.

@coderabbitai
Copy link

coderabbitai bot commented May 26, 2025

Walkthrough

The changes introduce comprehensive support for an "omit" feature across CRUD operations, allowing fields to be excluded from query results and arguments. An "upsert" operation is implemented in both the runtime client and type system, with validation, handler logic, and tests. Existing create, update, and delete operations are updated to handle the new omit option.

Changes

File(s) Change Summary
TODO.md Marked the "Upsert" task under ORM/Update as completed.
packages/runtime/src/client/client-impl.ts Renamed parameter throwIfNotFound to throwIfNoResult in createPromise. Added an upsert method to ModelOperations, calling the new upsert operation handler.
packages/runtime/src/client/crud-types.ts Added generic omit support to result and argument types. Extended CRUD argument types and ModelOperations interface with omit and upsert. Introduced UpsertArgs.
packages/runtime/src/client/crud/operations/base.ts Added 'upsert' to CrudOperation. Updated select/omit logic in read, trimResult, and needReturnRelations to use new SelectIncludeOmit type and handle omit.
packages/runtime/src/client/crud/operations/create.ts Updated runCreate to pass omit to readUnique, allowing created records to omit specified fields in the result.
packages/runtime/src/client/crud/operations/delete.ts Updated runDelete to pass omit to readUnique, enabling omission of specified fields from deleted record results.
packages/runtime/src/client/crud/operations/update.ts Added support for 'upsert' operation in UpdateOperationHandler with new runUpsert method. Updated runUpdate to support omit in read-back.
packages/runtime/src/client/crud/validator.ts Added upsert argument validation with validateUpsertArgs and schema builder. Enhanced create/update validation to support omit and improved select/include exclusivity.
packages/runtime/test/client-api/create.test.ts Added tests for create with omit and with combined include and omit options, verifying omitted fields are excluded and type errors are enforced.
packages/runtime/test/client-api/upsert.test.ts Added new test suite for upsert operation, covering creation, update, and nested relation scenarios for users and profiles.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant ModelOperations
    participant UpdateOperationHandler
    participant DB

    Client->>ModelOperations: upsert(args)
    ModelOperations->>UpdateOperationHandler: handle('upsert', args)
    UpdateOperationHandler->>DB: update(where, update)
    alt Record updated
        UpdateOperationHandler->>DB: readUnique(where, select/include/omit)
    else No record updated
        UpdateOperationHandler->>DB: create(create)
        UpdateOperationHandler->>DB: readUnique(where, select/include/omit)
    end
    UpdateOperationHandler->>ModelOperations: return result
    ModelOperations->>Client: return result
Loading

Poem

In the warren of code, new tunnels appear—
"Omit" lets us hide fields, so results are more clear.
Upsert hops in, both update and birth,
With tests and with types, it proves its true worth.
The rabbit now cheers, with a wiggle and grin:
"CRUD just got smarter—let the queries begin!"
🐇✨

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@ymc9 ymc9 merged commit 8ceca57 into dev May 26, 2025
1 check passed
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

🔭 Outside diff range comments (1)
packages/runtime/src/client/crud-types.ts (1)

96-141: ⚠️ Potential issue

Fix the default type parameter to improve type safety.

The static analysis tool correctly identifies that using {} as a default type is too permissive. Consider using a more specific default or removing it entirely.

Apply this diff to fix the type safety issue:

export type ModelResult<
    Schema extends SchemaDef,
    Model extends GetModels<Schema>,
-   Args extends SelectIncludeOmit<Schema, Model, boolean> = {},
+   Args extends SelectIncludeOmit<Schema, Model, boolean> = SelectIncludeOmit<Schema, Model, boolean>,
    Optional = false,
    Array = false
> = WrapType<

Alternatively, you could remove the default entirely if it's not necessary:

export type ModelResult<
    Schema extends SchemaDef,
    Model extends GetModels<Schema>,
-   Args extends SelectIncludeOmit<Schema, Model, boolean> = {},
+   Args extends SelectIncludeOmit<Schema, Model, boolean>,
    Optional = false,
    Array = false
> = WrapType<
🧰 Tools
🪛 Biome (1.9.4)

[error] 99-99: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🧹 Nitpick comments (1)
packages/runtime/test/client-api/create.test.ts (1)

36-61: Excellent test coverage for omit functionality!

The tests comprehensively verify both runtime behavior and TypeScript type safety for the omit feature. The use of @ts-expect-error correctly demonstrates that accessing omitted fields causes compilation errors.

However, remove the debugging console.log statements:

-            // @ts-expect-error
-            console.log(user2.name);
-            // @ts-expect-error
-            console.log(user3.name);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb02343 and 15a03e9.

📒 Files selected for processing (10)
  • TODO.md (1 hunks)
  • packages/runtime/src/client/client-impl.ts (3 hunks)
  • packages/runtime/src/client/crud-types.ts (12 hunks)
  • packages/runtime/src/client/crud/operations/base.ts (5 hunks)
  • packages/runtime/src/client/crud/operations/create.ts (1 hunks)
  • packages/runtime/src/client/crud/operations/delete.ts (1 hunks)
  • packages/runtime/src/client/crud/operations/update.ts (4 hunks)
  • packages/runtime/src/client/crud/validator.ts (6 hunks)
  • packages/runtime/test/client-api/create.test.ts (1 hunks)
  • packages/runtime/test/client-api/upsert.test.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
packages/runtime/src/client/client-impl.ts (1)
packages/runtime/src/client/crud/operations/update.ts (1)
  • UpdateOperationHandler (8-103)
packages/runtime/src/client/crud/operations/base.ts (2)
packages/runtime/src/client/crud-types.ts (1)
  • SelectIncludeOmit (325-333)
packages/runtime/src/schema/schema.ts (1)
  • GetModels (104-107)
packages/runtime/src/client/crud-types.ts (2)
packages/runtime/src/utils/type-utils.ts (1)
  • WrapType (11-15)
packages/runtime/src/schema/schema.ts (4)
  • NonRelationFields (171-182)
  • SchemaDef (12-19)
  • GetModels (104-107)
  • GetFields (121-124)
🪛 markdownlint-cli2 (0.17.2)
TODO.md

46-46: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

🪛 Biome (1.9.4)
packages/runtime/src/client/crud-types.ts

[error] 99-99: Don't use '{}' as a type.

Prefer explicitly define the object shape. '{}' means "any non-nullable value".

(lint/complexity/noBannedTypes)

🔇 Additional comments (26)
TODO.md (1)

46-46: LGTM!

The status update correctly reflects that upsert functionality has been implemented. The indentation appears consistent with other items in the list.

🧰 Tools
🪛 markdownlint-cli2 (0.17.2)

46-46: Unordered list indentation
Expected: 2; Actual: 4

(MD007, ul-indent)

packages/runtime/src/client/crud/operations/delete.ts (1)

34-34: LGTM!

The addition of omit support to the readUnique call is consistent with the broader implementation across CRUD operations. The change correctly passes through the omit option from the delete arguments.

packages/runtime/src/client/crud/operations/create.ts (1)

48-48: LGTM!

The addition of omit support brings consistency with the existing runCreateManyAndReturn method and follows the established pattern across CRUD operations.

packages/runtime/src/client/client-impl.ts (3)

269-269: Parameter rename improves clarity.

The rename from throwIfNotFound to throwIfNoResult better reflects the parameter's broader purpose across different operations.


278-278: Correctly uses the renamed parameter.

The conditional check properly uses the updated parameter name.


403-410: Well-implemented upsert method integration.

The upsert method follows the established pattern for CRUD operations:

  • Uses the correct operation type string 'upsert'
  • Delegates to UpdateOperationHandler which supports upsert operations
  • Enables post-processing appropriately
  • Correctly omits the throwIfNoResult parameter (defaults to false)

This integrates well with the broader upsert support implementation.

packages/runtime/test/client-api/upsert.test.ts (1)

1-77: Comprehensive and well-structured upsert test suite.

The test implementation covers key upsert scenarios effectively:

  1. Create scenario: Verifies upsert creates a new record when none exists, with nested profile creation
  2. Update scenario: Verifies upsert updates existing record, preserving related data
  3. ID update scenario: Tests updating the primary key field itself, which is an edge case worth covering

The test structure follows best practices:

  • Proper setup/teardown with schema pushing and client disconnection
  • Uses toMatchObject for partial matching, allowing for flexibility
  • Tests include/select functionality alongside upsert operations
  • Each scenario builds upon the previous state logically

The ID update test case is particularly valuable as it verifies that updating a field used in the where clause works correctly.

packages/runtime/src/client/crud/validator.ts (5)

22-22: Consistent upsert validation implementation.

The new validateUpsertArgs method and UpsertArgs type import follow the established pattern for other CRUD operation validators, maintaining consistency across the codebase.

Also applies to: 87-93


588-596: Enhanced create schema with omit support.

The create schema has been properly updated to:

  • Use specific schema builder methods (makeSelectSchema, makeIncludeSchema, makeOmitSchema)
  • Include the new omit field option
  • Apply mutual exclusivity refinement for select/include

This maintains consistency with the broader omit feature implementation.


609-610: Consistent omit support in createManyAndReturn schema.

The addition of include and omit options maintains consistency with other enhanced schemas.


911-913: Enhanced update schema with omit support.

The update schema properly includes the omit option and applies the same mutual exclusivity refinement pattern used in other schemas.

Also applies to: 917-917


930-943: Well-designed upsert schema validation.

The makeUpsertSchema method properly defines the validation requirements:

  • Required fields: where (unique), create, and update data
  • Optional fields: select, include, and omit with proper mutual exclusivity
  • Strict validation: Prevents extra properties
  • Consistent refinement: Applies the same select/include mutual exclusivity as other schemas

This schema correctly captures the upsert operation's requirements and maintains consistency with existing validation patterns.

packages/runtime/src/client/crud/operations/update.ts (4)

4-4: Proper imports for upsert support.

The addition of UpsertArgs type import and reordering of the BaseOperationHandler import maintain clean import organization.

Also applies to: 6-6


11-11: Correct integration of upsert operation handling.

The method signature properly extends the operation union type to include 'upsert', and the pattern matching correctly dispatches to the new runUpsert method with proper argument validation.

Also applies to: 23-27


42-42: Enhanced update operation with omit support.

The runUpdate method correctly includes the omit option when reading back the updated record, maintaining consistency with the broader omit feature implementation.


69-102: Robust upsert implementation with proper atomicity.

The runUpsert method implements solid upsert semantics:

Atomic transaction approach: Uses safeTransaction to ensure the update-or-create logic is atomic, preventing race conditions.

Correct upsert logic:

  1. Attempts update first with the provided where and update data
  2. If no record is affected (doesn't exist), creates a new record with create data
  3. Reads back the result with proper selection options

Proper error handling: Throws RejectedByPolicyError if the result cannot be read back due to policy restrictions.

Consistent patterns: Follows the same read-back pattern as other operations, supporting select, include, and omit options.

The update call parameters (undefined, true, false) correctly specify no additional options, allow missing records, and disable throwing on no result for the initial update attempt.

packages/runtime/src/client/crud/operations/base.ts (4)

32-32: LGTM! Import change aligns with the new type system.

The import change from SelectInclude to SelectIncludeOmit correctly reflects the enhanced type that now includes support for the omit field.


62-62: LGTM! Correctly adds 'upsert' to supported operations.

The addition of 'upsert' to the CrudOperation union type properly extends the supported operations.


168-174: LGTM! Correctly implements mutual exclusivity between select and omit.

The logic properly handles the mutual exclusivity between select and omit options:

  • When select is provided, it takes precedence
  • When select is not provided, all scalar fields are included except those specified in omit

This implementation aligns with common ORM patterns for field selection.


1789-1789: LGTM! Method signatures correctly updated to use SelectIncludeOmit.

The parameter type updates for trimResult and needReturnRelations methods are consistent with the broader changes to support the omit feature throughout the codebase.

Also applies to: 1802-1802

packages/runtime/src/client/crud-types.ts (6)

42-55: LGTM! DefaultModelResult correctly implements field omission.

The type correctly filters out omitted fields using conditional types. Fields are excluded from the result when they are present in the Omit type parameter with a value of true.


57-94: LGTM! ModelSelectResult properly handles omit with select.

The type correctly filters out omitted fields even when they are explicitly selected, ensuring consistent behavior across the API.


325-333: LGTM! SelectIncludeOmit type properly extends query options.

The renamed type correctly adds the omit field alongside existing select and include options, using the appropriate OmitFields type for field mapping.


562-563: LGTM! Consistently adds omit support across all operation types.

The omit field is correctly added to all relevant operation argument types. The CreateManyAndReturnArgs type properly excludes the include option using TypeScript's Omit utility type, which is appropriate for batch operations.

Also applies to: 574-574, 693-693, 789-789


705-715: LGTM! UpsertArgs type is well-structured.

The new UpsertArgs type correctly defines all necessary fields for an upsert operation:

  • create for the insert case
  • update for the update case
  • where for identifying the record
  • Optional select, include, and omit for controlling the returned data

1133-1135: LGTM! Upsert method correctly added to ModelOperations.

The upsert method signature follows the established pattern of other operations in the interface, properly using SelectSubset for type safety and returning the appropriate ModelResult type.

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