Skip to content

New Features#55

Closed
joshunrau wants to merge 38 commits intomainfrom
dev
Closed

New Features#55
joshunrau wants to merge 38 commits intomainfrom
dev

Conversation

@joshunrau
Copy link
Collaborator

@joshunrau joshunrau commented Jun 10, 2025

  • Allow users to define custom prisma extensions
  • Automatically extract JSON schema from Zod types to generate OpenAPI schema documentation

Summary by CodeRabbit

  • New Features

    • Introduced a Prisma client extension adding an exists method to all models and exposing model name metadata.
    • Added a utility to apply Swagger metadata to DTO classes based on Zod schemas.
  • Improvements

    • Enhanced Prisma module configuration for explicit client injection and extension.
    • Refined type safety and flexibility for Prisma client options and extensions.
    • Updated Prisma-related package dependencies to latest versions.
    • Applied Swagger metadata alongside validation schemas in decorators and DTOs.
    • Simplified and centralized user-related types in authentication and configuration modules.
    • Centralized locale and runtime environment typing for consistent usage across modules.
    • Adjusted authentication endpoint URL for improved compatibility.
  • Bug Fixes

    • Fixed authentication endpoint URL by removing redundant path segment.
  • Tests

    • Added and updated tests for Prisma client extensions, types, and module configuration.
    • Improved mocking and test setup for Prisma-related modules.
    • Added tests verifying Swagger metadata application error handling.
    • Added test verifying DTO class naming behavior.
  • Refactor

    • Streamlined and consolidated Prisma type exports and configuration.
    • Replaced internal Prisma factory logic with a more extensible extension-based approach.
    • Updated generic type parameters and configuration types for Prisma client and app container.
    • Simplified authentication module generics and centralized user types.
    • Removed direct dependency on zod generics in authentication module setup.
    • Simplified and unified type imports and usage for environment, locale, and user payloads.
  • Chores

    • Removed obsolete Prisma factory and related test files.
    • Updated internal type and configuration handling for user config and testing utilities.
    • Adjusted imports and type references for environment and Prisma client options.
    • Added mock utilities for Prisma extensions in testing.

@coderabbitai
Copy link

coderabbitai bot commented Jun 10, 2025

Caution

Review failed

The pull request is closed.

Walkthrough

This update refactors Prisma client integration by introducing a new extension mechanism, replacing the previous factory-based approach. Types and configuration generics are updated for clarity and flexibility. The code now explicitly injects the Prisma client constructor and options, and adds Swagger metadata application to DTOs and validation decorators. Several new and revised tests accompany these changes.

Changes

File(s) Change Summary
src/modules/prisma/prisma.extensions.ts Added new Prisma extension with exists method and model name metadata; exports new types.
src/modules/prisma/prisma.factory.ts, src/modules/prisma/tests/prisma.factory.test.ts Removed PrismaFactory, its types, and related tests; replaced with extension-based approach.
src/modules/prisma/prisma.config.ts Refactored config types: new generics, explicit client constructor, removed omit.
src/modules/prisma/prisma.module.ts Switched to direct Prisma client instantiation and extension; removed factory provider.
src/modules/prisma/prisma.types.ts Introduced new type inference utilities for Prisma extensions; redefined ExtendedPrismaClient.
src/modules/prisma/prisma.service.ts, example/cats/cats.service.ts Changed injected Prisma client type from ExtendedPrismaClient to PrismaClientLike.
src/app/app.factory.ts, src/app/app.container.ts, src/user-config.ts Updated AppFactory, AppContainer, and user config generics for new Prisma client types and options.
src/index.ts Consolidated and updated Prisma-related type exports.
src/testing/helpers/e2e.ts Updated AppContainer generics in helper signature.
src/testing/helpers/integration.ts, example/app.ts, src/modules/prisma/tests/prisma.module.test.ts Explicitly inject Prisma client constructor in test and example configs.
src/testing/mocks/prisma.module.mock.ts Added mock for defineExtension.
src/utils/validation.utils.ts Added applySwaggerMetadata function for Swagger property metadata from Zod schemas.
src/decorators/validation-schema.decorator.ts, src/mixins/data-transfer-object.mixin.ts Applied Swagger metadata in validation schema and DTO mixin decorators.
src/utils/tests/validation.utils.test.ts Added tests for applySwaggerMetadata error handling.
src/modules/prisma/tests/prisma.extensions.test.ts, src/modules/prisma/tests/prisma.types.test.ts Added new tests for Prisma extension and typings.
package.json Bumped Prisma-related package versions from 6.5.0 to 6.9.0.
.bruno/collection.bru Changed auth endpoint URL in axios POST request.

Sequence Diagram(s)

sequenceDiagram
    participant App as AppFactory
    participant Prisma as PrismaModule
    participant Client as PrismaClient
    participant Extension as LibnestPrismaExtension

    App->>Prisma: Pass config with client.constructor and options
    Prisma->>Client: Instantiate PrismaClient with options
    Prisma->>Extension: Apply LibnestPrismaExtension to client
    Extension->>Client: Add $allModels.exists and model name metadata
    Client-->>Prisma: Extended client returned
    Prisma-->>App: Provide extended client to app
Loading

Possibly related PRs

Suggested labels

released

Poem

Prisma’s cloak is woven anew,
Extensions bloom—exists shines through!
No more factories, mocks in line,
Swagger tags now intertwine.
Types refined, the tests expand,
All in sync, just as planned.
Onward, code—your future’s grand! 🚀


📜 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 1c5ba88 and c5f8a0e.

📒 Files selected for processing (2)
  • src/utils/__tests__/validation.utils.test.ts (1 hunks)
  • src/utils/validation.utils.ts (2 hunks)
✨ 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.

@codecov
Copy link

codecov bot commented Jun 10, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 100.00%. Comparing base (7993134) to head (84c702d).
Report is 640 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##              main       #55     +/-   ##
===========================================
  Coverage   100.00%   100.00%             
===========================================
  Files           39        82     +43     
  Lines          878      2230   +1352     
  Branches       176       405    +229     
===========================================
+ Hits           878      2230   +1352     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

🔭 Outside diff range comments (1)
example/cats/cats.service.ts (1)

15-16: ⚠️ Potential issue

crypto.randomUUID() is used without importing node:crypto.

randomUUID is not available on the global crypto object in every supported Node LTS release. Import crypto explicitly to avoid runtime failures on older runtimes and to satisfy TS type-checking when dom libs are not enabled.

-import { Injectable, InternalServerErrorException } from '@nestjs/common';
+import { Injectable, InternalServerErrorException } from '@nestjs/common';
+import crypto from 'node:crypto';
🧹 Nitpick comments (12)
.bruno/collection.bru (1)

16-22: DRY up the base-URL logic

baseUrl is hard-coded a second time rather than re-using the earlier variable or getVar('BASE_URL'). If the port or host changes, this duplication will silently break requests.

-  const baseUrl = 'http://localhost:5500';
+  const baseUrl = getVar('BASE_URL');
src/testing/mocks/prisma.module.mock.ts (1)

11-17: Align mock signature with Prisma’s typings

defineExtension currently returns its argument untyped (unknown). Returning arg directly works, but exporting vi.fn<Parameters<typeof Prisma.defineExtension>[0], ReturnType<typeof Prisma.defineExtension>>() (or at least any) keeps TypeScript hints correct and prevents future mis-use.

Not blocking, just a touch-up if you want full type safety.

example/app.ts (1)

3-4: Avoid paying the @prisma/client size tax in the browser bundle

Because this file is part of an example/server build the import is fine, but if the same code ever hits a client-side bundle @prisma/client will balloon the size. Consider a dynamic import (await import('@prisma/client')) or server-only barrel export to keep the split clear.

src/testing/helpers/integration.ts (1)

33-35: Hard-coding the constructor path couples tests to Prisma internals.

Passing { constructor: PrismaClient } is fine, but why not expose the constructor through an injector token or helper to keep the config agnostic to Prisma’s concrete class? This would also align with the new PrismaClientLike abstraction.

src/mixins/data-transfer-object.mixin.ts (1)

4-5: Duplication with ValidationSchema decorator.

Both this mixin and ValidationSchema now call applyValidationSchema and applySwaggerMetadata. Consider extracting a small helper (e.g., decorateWithValidationAndSwagger) to avoid repeating the same two calls in multiple places.

src/decorators/validation-schema.decorator.ts (1)

22-24: Sequence is fine, but order dependency should be documented.

If future updates introduce side effects to either applyValidationSchema or applySwaggerMetadata, developers might wonder whether order matters. A short comment stating the order is irrelevant (or required) would prevent accidental regressions.

src/modules/prisma/prisma.service.ts (2)

9-13: Type now too loose – consider narrowing PrismaClientLike
PrismaClientLike is essentially PrismaClient & { [key: string]: any }, which wipes out most static-type safety on the injected client. If the service really only needs the extended client produced by your new extension, inject that more specific alias (e.g. ExtendedPrismaClient) instead and expose the broad “like” type only where you truly accept arbitrary augmentation.


13-13: Constructor param can remain private readonly
Nothing else in the class mutates client; marking it private readonly both hides the field from consumers and prevents accidental reassignment.

-  constructor(@Inject(PRISMA_CLIENT_TOKEN) public readonly client: PrismaClientLike) {}
+  constructor(@Inject(PRISMA_CLIENT_TOKEN) private readonly client: PrismaClientLike) {}
src/testing/helpers/e2e.ts (1)

18-18: Still leaking any – tighten generics or add defaults
AppContainer<any, any, any> drops the compile-time guarantees you just worked to introduce. Consider threading the concrete types into the helper (or at least default them to unknown):

-export function e2e(appContainer: AppContainer<any, any, any>, fn: EndToEndTestFactory): void {
+export function e2e<
+  TEnv,
+  TOpts,
+  TClient
+>(appContainer: AppContainer<TEnv, TOpts, TClient>, fn: EndToEndTestFactory): void {

This keeps the helper fully generic without losing type information.

src/modules/prisma/__tests__/prisma.types.test.ts (1)

10-33: Static-analysis noise – replace banned {} placeholders
Biome flags the six occurrences of the bare {} type. They can be swapped for Record<string, never> (or a more precise signature) without affecting the assertion semantics:

-          client: {};
+          client: Record<string, never>;
...
-          query: {};
-          result: {};
+          query: Record<string, never>;
+          result: Record<string, never>;

Add a // biome-ignore lint/complexity/noBannedTypes comment if you intentionally rely on {}.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)

src/app/app.container.ts (1)

15-17: Consider default generic parameters
If most consumers use the default Prisma options & client, add default types to reduce noise when instantiating AppContainer:

export class AppContainer<
  TEnv extends BaseEnv,
  _TPrismaClientOptions extends DefaultPrismaClientOptions = DefaultPrismaClientOptions,
  _TExtendedPrismaClient extends LibnestExtendedPrismaClient = LibnestExtendedPrismaClient
> ...

This keeps full flexibility while improving ergonomics.

src/utils/validation.utils.ts (1)

26-29: Consider extracting additional schema metadata.

The current implementation only extracts the type field. Consider adding more metadata for richer API documentation.

     const propertyMetadata = {
-      type: propertySchema.type
+      type: propertySchema.type,
+      description: propertySchema.description,
+      example: propertySchema.examples?.[0],
+      required: baseSchema.required?.includes(propertyKey)
     } as ApiPropertyOptions;
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 49f3ce7 and 84c702d.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (26)
  • .bruno/collection.bru (1 hunks)
  • example/app.ts (2 hunks)
  • example/cats/cats.service.ts (1 hunks)
  • package.json (3 hunks)
  • src/app/__tests__/app.factory.test.ts (2 hunks)
  • src/app/app.container.ts (1 hunks)
  • src/app/app.factory.ts (1 hunks)
  • src/decorators/validation-schema.decorator.ts (2 hunks)
  • src/index.ts (1 hunks)
  • src/mixins/data-transfer-object.mixin.ts (2 hunks)
  • src/modules/prisma/__tests__/prisma.extensions.test.ts (1 hunks)
  • src/modules/prisma/__tests__/prisma.factory.test.ts (0 hunks)
  • src/modules/prisma/__tests__/prisma.module.test.ts (2 hunks)
  • src/modules/prisma/__tests__/prisma.types.test.ts (1 hunks)
  • src/modules/prisma/prisma.config.ts (1 hunks)
  • src/modules/prisma/prisma.extensions.ts (1 hunks)
  • src/modules/prisma/prisma.factory.ts (0 hunks)
  • src/modules/prisma/prisma.module.ts (2 hunks)
  • src/modules/prisma/prisma.service.ts (1 hunks)
  • src/modules/prisma/prisma.types.ts (2 hunks)
  • src/testing/helpers/e2e.ts (1 hunks)
  • src/testing/helpers/integration.ts (2 hunks)
  • src/testing/mocks/prisma.module.mock.ts (1 hunks)
  • src/user-config.ts (2 hunks)
  • src/utils/__tests__/validation.utils.test.ts (1 hunks)
  • src/utils/validation.utils.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • src/modules/prisma/prisma.factory.ts
  • src/modules/prisma/tests/prisma.factory.test.ts
🧰 Additional context used
🧬 Code Graph Analysis (15)
src/mixins/data-transfer-object.mixin.ts (1)
src/utils/validation.utils.ts (1)
  • applySwaggerMetadata (12-31)
example/cats/cats.service.ts (4)
src/modules/prisma/prisma.service.ts (1)
  • Injectable (11-47)
src/index.ts (2)
  • InjectPrismaClient (39-39)
  • PrismaClientLike (41-41)
src/modules/prisma/prisma.decorators.ts (1)
  • InjectPrismaClient (12-14)
src/modules/prisma/prisma.types.ts (1)
  • PrismaClientLike (41-43)
src/testing/helpers/integration.ts (1)
src/testing/mocks/prisma.module.mock.ts (1)
  • PrismaClient (5-9)
src/modules/prisma/__tests__/prisma.module.test.ts (1)
src/testing/mocks/prisma.module.mock.ts (1)
  • PrismaClient (5-9)
src/utils/__tests__/validation.utils.test.ts (1)
src/utils/validation.utils.ts (1)
  • applySwaggerMetadata (12-31)
src/modules/prisma/__tests__/prisma.types.test.ts (3)
src/modules/prisma/prisma.types.ts (2)
  • ExtendedPrismaClient (66-66)
  • RuntimePrismaClientOptions (60-64)
src/index.ts (1)
  • ExtendedPrismaClient (41-41)
src/testing/mocks/prisma.module.mock.ts (1)
  • Prisma (11-17)
example/app.ts (1)
src/testing/mocks/prisma.module.mock.ts (1)
  • PrismaClient (5-9)
src/modules/prisma/prisma.service.ts (1)
src/modules/prisma/prisma.types.ts (1)
  • PrismaClientLike (41-43)
src/testing/helpers/e2e.ts (1)
src/app/app.container.ts (1)
  • AppContainer (13-43)
src/app/app.container.ts (3)
src/schemas/env.schema.ts (1)
  • BaseEnv (40-40)
src/modules/prisma/prisma.config.ts (1)
  • DefaultPrismaClientOptions (8-8)
src/modules/prisma/prisma.extensions.ts (1)
  • LibnestExtendedPrismaClient (48-48)
src/decorators/validation-schema.decorator.ts (1)
src/utils/validation.utils.ts (1)
  • applySwaggerMetadata (12-31)
src/modules/prisma/prisma.module.ts (3)
src/modules/prisma/connection.factory.ts (1)
  • MongoConnection (9-11)
src/modules/prisma/prisma.config.ts (1)
  • PrismaModuleOptions (10-21)
src/modules/prisma/prisma.extensions.ts (1)
  • LibnestPrismaExtension (22-46)
src/modules/prisma/__tests__/prisma.extensions.test.ts (2)
src/testing/mocks/prisma.module.mock.ts (1)
  • Prisma (11-17)
src/modules/prisma/prisma.extensions.ts (1)
  • exists (34-40)
src/user-config.ts (2)
src/modules/prisma/prisma.config.ts (1)
  • DefaultPrismaClientOptions (8-8)
src/modules/prisma/prisma.extensions.ts (1)
  • LibnestExtendedPrismaClient (48-48)
src/app/app.factory.ts (5)
src/modules/prisma/prisma.config.ts (2)
  • DefaultPrismaClientOptions (8-8)
  • PrismaModuleOptions (10-21)
src/modules/prisma/prisma.extensions.ts (1)
  • LibnestExtendedPrismaClient (48-48)
src/app/app.base.ts (3)
  • AppContainerParams (22-27)
  • ConditionalImport (17-20)
  • ImportedModule (15-15)
src/interceptors/render.interceptor.tsx (1)
  • JSXOptions (15-20)
src/app/app.container.ts (1)
  • AppContainer (13-43)
🪛 Biome (1.9.4)
src/modules/prisma/__tests__/prisma.types.test.ts

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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)

src/modules/prisma/prisma.types.ts

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

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

(lint/complexity/noBannedTypes)

🔇 Additional comments (20)
.bruno/collection.bru (1)

20-23: Confirm the new endpoint path (/auth/login) is live

Removing /v1 is fine so long as every environment has already dropped the versioned path. Double-check staging/production before merging to avoid 401s at runtime.

package.json (1)

63-69: Run prisma generate after bumping to 6.9.x

@prisma/engines, @prisma/get-platform, and @prisma/client must stay in lock-step with the generated client. Please run:

pnpm prisma:generate

and commit any regenerated artifacts to guarantee CI and consumers see a compatible client.

Also applies to: 94-96, 134-135

example/app.ts (1)

58-63: No immediate issues – explicit constructor injection looks good

Passing the constructor via prisma.client cleanly decouples instantiation and is consistent with the new module API.

src/modules/prisma/__tests__/prisma.module.test.ts (2)

3-4: Ensure @prisma/client is mocked before this import

The assertion later depends on __isMockPrismaClient, which the real Prisma client obviously lacks. Make sure the Vitest module-alias that points @prisma/clientsrc/testing/mocks/prisma.module.mock.ts executes before this line; otherwise the test will silently instantiate the real client and the assertion will fail.


34-37: Constructor injection test update LGTM

The test now mirrors production configuration – nice catch.

src/utils/__tests__/validation.utils.test.ts (1)

7-12: Nice negative test case addition.

The new test correctly asserts that non-object schemas trigger InternalServerErrorException. Good coverage.

src/index.ts (1)

41-41: Export list LGTM
Re-exporting the new Prisma types here keeps the public surface cohesive. No issues spotted.

src/app/app.container.ts (1)

8-10: Import block looks good
New type imports align with the revised generics; nothing to change here.

src/modules/prisma/prisma.module.ts (1)

7-7: Import changes align with new extension architecture.

The imports correctly reference the new extension mechanism and updated type exports.

Also applies to: 13-13

src/app/__tests__/app.factory.test.ts (2)

47-55: Mock implementation correctly simulates Prisma client.

The mock provides all necessary methods for testing the new client instantiation pattern.


133-150: Test properly validates custom Prisma configuration.

The test correctly verifies that the client constructor is called with merged options including the constructed datasourceUrl.

src/modules/prisma/__tests__/prisma.extensions.test.ts (2)

10-17: Test setup properly mocks Prisma internals.

The mock setup correctly simulates Prisma's extension mechanism for testing.


26-60: Comprehensive test coverage for extension functionality.

The tests thoroughly validate both the exists method behavior and the computed model name functionality.

src/modules/prisma/prisma.extensions.ts (2)

7-20: Type definitions properly structure the extension interface.

The type definitions clearly specify the added methods and properties.


34-40:

Details

✅ Verification successful

Type casting in exists method bypasses type safety.

The double type casting through unknown could hide type mismatches. Consider verifying this approach is necessary.


🌐 Web query:

Is there a type-safe way to access Prisma extension context without using "as unknown as" type casting?

💡 Result:

Accessing Prisma extension context in a type-safe manner is challenging due to inherent TypeScript limitations, particularly with how TypeScript handles generics and the this keyword in Prisma extensions.

The Challenge with Prisma Extension Context

When working with Prisma extensions, typing the context properly is difficult because TypeScript cannot automatically infer the type of this when applying a generic type to it[1]. This becomes especially problematic when trying to access the extension context in a type-safe way.

The issue typically arises in scenarios like this:

export const UserModel = Prisma.defineExtension({
  model: {
    $allModels: {
      async exists<T extends PrismaModel>(
        this: T,
        where: NonNullable<Prisma.Args<T, 'findFirst'>['where']>,
      ) {
        const context = Prisma.getExtensionContext(this) 
        // Type safety issues occur here
      },
    },
  },
})

Current Workarounds

1. Using Type Casting

The most common workaround is to use type casting with as unknown as:

const context = Prisma.getExtensionContext(this) as unknown as T

This approach works but sacrifices true type safety[7][10].

2. Helper Function Approach

A more elegant solution involves creating helper functions that preserve type safety:

function getExtendedClient() {
  const client = () => new PrismaClient().$extends({
    // your extension configuration
  })
  return class {
    constructor() {
      return client()
    }
  } as (new () => ReturnType<typeof client>)
}

This approach, suggested by the Prisma team, allows you to create wrapper classes while maintaining type safety[7].

3. Using Prisma Type Utilities

Prisma offers type utilities like Args, Result, Payload, and Exact that can help with creating type-safe extensions[2][4]. These utilities adapt to your models dynamically:

type PostCreateBody = Prisma.Args<typeof prisma.post, 'create'>['data']

const addPost = async (postBody: PostCreateBody) => {
  const post = await prisma.post.create({ data: postBody })
  return post
}

Alternative Approaches

Creating Extended Client Types

You can create an extended client type and use it consistently:

// Define your extended client
function extendPrismaClient() {
  const prisma = new PrismaClient();
  return prisma.$extends({
    // your extensions
  });
}

// Create a class that can be instantiated with the extended type
const ExtendedPrismaClient = class {
  constructor() {
    return extendPrismaClient();
  }
} as new () => ReturnType<typeof extendPrismaClient>;

// Use in your service
@Injectable()
export class PrismaService extends ExtendedPrismaClient {}

This pattern is particularly useful in frameworks like NestJS[7].

Conclusion

Unfortunately, there is currently no perfect type-safe way to access Prisma extension context without using some form of type casting. This limitation is acknowledged by the Prisma team as a result of TypeScript's constraints when dealing with generic types and the this keyword[1].

The recommended approach is to use type casting within the extension implementation while ensuring that the public API of your extension remains fully type-safe. Alternatively, you can use the helper function pattern or Prisma's built-in type utilities to minimize type safety compromises.

As the Prisma ecosystem evolves, better solutions may emerge, but for now, these workarounds represent the best available options for balancing type safety with extension functionality.

Citations:


Prisma extension context typing requires type casting.

Due to TypeScript’s limitations around generics and this inference, there isn’t a fully type-safe way to call Prisma.getExtensionContext(this) without a cast. The current as unknown as { … } pattern is the standard workaround. If you’d like to minimize inline casts, you can:

  • Extract the cast into a small helper, e.g.
    function getCtx<T>(obj: T) {
      return Prisma.getExtensionContext(obj) as unknown as {
        findFirst: (...args: unknown[]) => Promise<unknown>;
      };
    }
  • Or use the “extended client” wrapper pattern suggested by Prisma to preserve type safety at the public API boundary.

Given these TS constraints, the existing exists implementation is appropriate.

src/user-config.ts (1)

5-6: LGTM!

The type updates correctly align with the new Prisma extension architecture, replacing the global omit config with explicit client options and extended client types.

Also applies to: 55-56, 62-63

src/modules/prisma/prisma.config.ts (1)

1-1: Well-structured Prisma configuration refactor!

The new client-based configuration provides better flexibility for custom extensions while maintaining type safety with NoInfer.

Also applies to: 8-8, 14-18

src/app/app.factory.ts (1)

28-29: Consistent integration with new Prisma types!

The factory correctly propagates the new generic parameters throughout the application container hierarchy.

Also applies to: 37-37, 43-47, 79-79

src/modules/prisma/prisma.types.ts (2)

16-39: Advanced type inference for Prisma extensions!

The utility types correctly handle the complex type merging required for Prisma client extensions. The {} usage on line 21 is intentional - it's the expected parameter type for MergeExtArgs.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/complexity/noBannedTypes)


60-66: Clean runtime type extraction!

The conditional type correctly extracts Prisma client options from user config with proper fallback.

Comment on lines +4 to 5
import { PrismaClient } from '@prisma/client';
import request from 'supertest';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Importing the real @prisma/client in tests may bypass the mock.

Unit/integration tests already provide a mock PrismaClient (src/testing/mocks/prisma.module.mock.ts). Importing the real class here risks an unexpected network connection or schema mismatch if the Vitest mock is not applied first. Prefer re-exporting the mock or importing via a path that Vitest can safely stub.

🤖 Prompt for AI Agents
In src/testing/helpers/integration.ts at lines 4-5, avoid importing the real
PrismaClient from '@prisma/client' because it can bypass the test mock and cause
unintended network calls or schema issues. Instead, import or re-export the
PrismaClient from the mock module at src/testing/mocks/prisma.module.mock.ts or
use a path that Vitest can stub safely to ensure the mock is applied correctly
during tests.

Comment on lines +37 to 44
inject: [MONGO_CONNECTION_TOKEN, PRISMA_MODULE_OPTIONS_TOKEN],
provide: PRISMA_CLIENT_TOKEN,
useFactory: (prismaFactory: PrismaFactory, { omit }: PrismaModuleOptions): ExtendedPrismaClient => {
return prismaFactory.createClient({ omit });
useFactory: (mongoConnection: MongoConnection, { client }: PrismaModuleOptions): ExtendedPrismaClient => {
return new client.constructor({
...client.options,
datasourceUrl: mongoConnection.url.href
}).$extends(LibnestPrismaExtension);
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Verify client constructor and options are defined.

The factory correctly implements the new client instantiation pattern. However, ensure that client.constructor and client.options are always defined to avoid runtime errors.


🏁 Script executed:

#!/bin/bash
# Description: Verify PrismaModuleOptions type ensures client.constructor is required

# Check the PrismaModuleOptions type definition
ast-grep --pattern 'type PrismaModuleOptions<$$$> = {
  client: {
    constructor: $$$
    $$$
  }
  $$$
}'

Length of output: 1092


Use a default when spreading client.options to prevent runtime errors

The PrismaModuleOptions type guarantees client.constructor is always defined, but client.options is optional. Spreading undefined will throw, so default it to an empty object.

• File: src/modules/prisma/prisma.module.ts (provider factory, lines 37–44)
• Update the spread of client.options:

           useFactory: (mongoConnection: MongoConnection, { client }: PrismaModuleOptions): ExtendedPrismaClient => {
-            return new client.constructor({
-              ...client.options,
+            return new client.constructor({
+              ...(client.options ?? {}),
               datasourceUrl: mongoConnection.url.href
             }).$extends(LibnestPrismaExtension);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
inject: [MONGO_CONNECTION_TOKEN, PRISMA_MODULE_OPTIONS_TOKEN],
provide: PRISMA_CLIENT_TOKEN,
useFactory: (prismaFactory: PrismaFactory, { omit }: PrismaModuleOptions): ExtendedPrismaClient => {
return prismaFactory.createClient({ omit });
useFactory: (mongoConnection: MongoConnection, { client }: PrismaModuleOptions): ExtendedPrismaClient => {
return new client.constructor({
...client.options,
datasourceUrl: mongoConnection.url.href
}).$extends(LibnestPrismaExtension);
}
inject: [MONGO_CONNECTION_TOKEN, PRISMA_MODULE_OPTIONS_TOKEN],
provide: PRISMA_CLIENT_TOKEN,
useFactory: (mongoConnection: MongoConnection, { client }: PrismaModuleOptions): ExtendedPrismaClient => {
return new client.constructor({
...(client.options ?? {}),
datasourceUrl: mongoConnection.url.href
}).$extends(LibnestPrismaExtension);
}
🤖 Prompt for AI Agents
In src/modules/prisma/prisma.module.ts between lines 37 and 44, the factory
function spreads client.options without ensuring it is defined, which can cause
runtime errors if client.options is undefined. To fix this, update the spread
operation to use a default empty object when client.options is undefined, for
example by spreading client.options || {} instead of client.options directly.

Comment on lines 12 to 31
export function applySwaggerMetadata<T extends z.ZodType<{ [key: string]: any }>>(
target: Class<z.output<T>>,
schema: T
): void {
const baseSchema = z.toJSONSchema(schema, {
io: 'input',
target: 'draft-7',
unrepresentable: 'any'
}) as z.core.JSONSchema.ObjectSchema;
if (!(baseSchema.type === 'object')) {
throw new InternalServerErrorException(`Expected schema to be of type 'object', not '${baseSchema.type}'`);
}
for (const propertyKey in baseSchema.properties) {
const propertySchema: SchemaObject = baseSchema.properties[propertyKey]!;
const propertyMetadata = {
type: propertySchema.type
} as ApiPropertyOptions;
createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add null check for property schema access.

The non-null assertion on line 25 could cause runtime errors if a property is undefined.

-    const propertySchema: SchemaObject = baseSchema.properties[propertyKey]!;
+    const propertySchema: SchemaObject | undefined = baseSchema.properties[propertyKey];
+    if (!propertySchema) continue;
🤖 Prompt for AI Agents
In src/utils/validation.utils.ts around lines 12 to 31, the code uses a non-null
assertion when accessing baseSchema.properties[propertyKey], which can cause
runtime errors if the property is undefined. Modify the code to add a null or
undefined check before accessing propertySchema.type, and only proceed with
creating the API property decorator if propertySchema is defined.

Comment on lines +23 to +30
const result = {} as LibnestPrismaExtensionArgs['result'];
Object.keys(Prisma.ModelName).forEach((modelName) => {
result[getModelKey(modelName)] = {
__modelName: {
compute: (): string => modelName
}
};
});
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Type inconsistency in compute function return type.

The compute function returns string but the type definition expects it to return the specific model name type K.

-        compute: (): string => modelName
+        compute: () => modelName as any
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const result = {} as LibnestPrismaExtensionArgs['result'];
Object.keys(Prisma.ModelName).forEach((modelName) => {
result[getModelKey(modelName)] = {
__modelName: {
compute: (): string => modelName
}
};
});
const result = {} as LibnestPrismaExtensionArgs['result'];
Object.keys(Prisma.ModelName).forEach((modelName) => {
result[getModelKey(modelName)] = {
__modelName: {
compute: () => modelName as any
}
};
});
🤖 Prompt for AI Agents
In src/modules/prisma/prisma.extensions.ts around lines 23 to 30, the compute
function currently returns a string but should return the specific model name
type K as per the type definition. Update the compute function's return type to
match the expected generic type K instead of a plain string, ensuring type
consistency with the LibnestPrismaExtensionArgs['result'] definition.

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

🧹 Nitpick comments (2)
src/modules/prisma/__tests__/prisma.types.test.ts (1)

12-20: Consider replacing {} with Record<string, never> to silence Biome’s noBannedTypes rule

The banned-type linter fires six times here.
If project policy enforces that rule, swapping to Record<string, never> (or a more specific shape) will appease it without altering semantics.
Otherwise, ignore.

Also applies to: 26-33

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)

src/modules/prisma/prisma.types.ts (1)

18-22: Empty object literal in MergeExtArgs triggers lint – use Record<string, never>

Biome flags '{}' as an unsafe “any non-nullable value”.
Unless you actually rely on that wideness, tighten the type:

-  {},
+  Record<string, never>,

This keeps intent clear and avoids linter noise.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/complexity/noBannedTypes)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 84c702d and 38b2796.

📒 Files selected for processing (8)
  • src/app/app.factory.ts (1 hunks)
  • src/modules/config/__tests__/config.service.test.ts (1 hunks)
  • src/modules/config/config.module.ts (1 hunks)
  • src/modules/config/config.service.ts (1 hunks)
  • src/modules/prisma/__tests__/prisma.types.test.ts (1 hunks)
  • src/modules/prisma/prisma.types.ts (2 hunks)
  • src/schemas/env.schema.ts (0 hunks)
  • src/user-types.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • src/schemas/env.schema.ts
✅ Files skipped from review due to trivial changes (4)
  • src/modules/config/config.service.ts
  • src/modules/config/tests/config.service.test.ts
  • src/modules/config/config.module.ts
  • src/user-types.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/app/app.factory.ts
🧰 Additional context used
🪛 Biome (1.9.4)
src/modules/prisma/prisma.types.ts

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

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

(lint/complexity/noBannedTypes)

src/modules/prisma/__tests__/prisma.types.test.ts

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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)


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

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

(lint/complexity/noBannedTypes)

🪛 GitHub Check: ci
src/modules/prisma/__tests__/prisma.types.test.ts

[failure] 1-1:
Module '"@prisma/client"' has no exported member 'Prisma'.

🔇 Additional comments (2)
src/modules/prisma/prisma.types.ts (2)

15-22: Verify that Prisma.Extension is the intended generic

Prisma’s public API currently exposes Prisma.ExtensionArgs; Prisma.Extension is internal and may change without notice.
If you meant the public helper, swap accordingly to future-proof the definition.

🧰 Tools
🪛 Biome (1.9.4)

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

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

(lint/complexity/noBannedTypes)


34-38: Nice abstraction layer

InferExtendedClient neatly composes the extension, type map callback and ext-args – concise and reusable. 👍

Comment on lines +15 to +17
$allModels: {
readonly exists: () => <T>(this: T, where: Prisma.Args<T, 'findFirst'>['where']) => Promise<boolean>;
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

exists is typed as a function that returns a function – drop the extra arrow

exists: () => <T>( … ) => Promise<boolean> describes a higher-order function, but the extension is meant to expose a single method.
Remove the outer () =>:

-              readonly exists: () => <T>(this: T, where: Prisma.Args<T, 'findFirst'>['where']) => Promise<boolean>;
+              readonly exists: <T>(this: T, where: Prisma.Args<T, 'findFirst'>['where']) => Promise<boolean>;

Apply the same change in the second occurrence (lines 29-30).
Without this, the inferred type won’t match the real implementation and the compile-time test becomes vacuous.

Also applies to: 28-30

🤖 Prompt for AI Agents
In src/modules/prisma/__tests__/prisma.types.test.ts around lines 15-17 and
28-30, the `exists` property is incorrectly typed as a higher-order function
returning another function. Remove the outer arrow function so that `exists` is
directly typed as a generic function returning a Promise<boolean>. This change
aligns the type with the actual implementation and ensures the compile-time test
is meaningful.

@@ -0,0 +1,37 @@
import { Prisma } from '@prisma/client';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Change the Prisma import to a type-only import

The CI failure (has no exported member 'Prisma') stems from importing the runtime namespace value.
Use a type-only import so TS pulls only the declaration and emits no JS.

-import { Prisma } from '@prisma/client';
+import type { Prisma } from '@prisma/client';
🧰 Tools
🪛 GitHub Check: ci

[failure] 1-1:
Module '"@prisma/client"' has no exported member 'Prisma'.

🤖 Prompt for AI Agents
In src/modules/prisma/__tests__/prisma.types.test.ts at line 1, change the
import of Prisma from a regular import to a type-only import by adding the
'type' keyword before the import. This ensures TypeScript imports only the type
declarations without including any runtime code, preventing the CI error about
'Prisma' not being an exported member.

BREAKING CHANGE: Use UserTypes to declare JwtPayload and UserQueryMetadata
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

🧹 Nitpick comments (3)
example/app.ts (1)

23-24: Consider tracking this type safety issue

The type assertion bypasses TypeScript's type checking. While the comment explains why, this should be tracked for resolution when migrating to a monorepo.

Would you like me to create an issue to track this technical debt?

src/user-config.ts (1)

71-71: Replace empty interfaces with type aliases

Empty interfaces are equivalent to {}. Use type aliases for clarity.

-export namespace UserTypes {
-  export interface Locales {}
+export namespace UserTypes {
+  export type Locales = {};
-  export interface UserQueryMetadata {}
+  export type UserQueryMetadata = {};

Also applies to: 75-75

🧰 Tools
🪛 Biome (1.9.4)

[error] 71-71: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

src/modules/auth/auth.module.ts (1)

68-68: Use class name instead of super in static context

Using super in static methods can be confusing. Use the explicit class name.

-return super.forRoot(options);
+return ConfigurableAuthModule.forRoot(options);
-return super.forRootAsync(options);
+return ConfigurableAuthModule.forRootAsync(options);

Also applies to: 73-73

🧰 Tools
🪛 Biome (1.9.4)

[error] 68-68: Using super in a static context can be confusing.

super refers to a parent class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 38b2796 and 782f293.

📒 Files selected for processing (10)
  • example/app.ts (3 hunks)
  • src/middleware/accept-language.middleware.ts (1 hunks)
  • src/modules/auth/__tests__/auth.module.spec.ts (1 hunks)
  • src/modules/auth/auth.config.ts (2 hunks)
  • src/modules/auth/auth.module.ts (1 hunks)
  • src/modules/auth/auth.service.ts (2 hunks)
  • src/modules/auth/strategies/jwt.strategy.ts (2 hunks)
  • src/typings/globals.ts (1 hunks)
  • src/user-config.ts (2 hunks)
  • src/user-types.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • src/typings/globals.ts
  • src/middleware/accept-language.middleware.ts
  • src/modules/auth/auth.service.ts
  • src/modules/auth/strategies/jwt.strategy.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/user-types.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
example/app.ts (1)
src/testing/mocks/prisma.module.mock.ts (1)
  • PrismaClient (5-9)
src/modules/auth/auth.config.ts (1)
src/user-config.ts (2)
  • JwtPayload (72-74)
  • UserQueryMetadata (75-75)
src/user-config.ts (4)
src/modules/prisma/prisma.config.ts (1)
  • DefaultPrismaClientOptions (8-8)
src/modules/prisma/prisma.extensions.ts (1)
  • LibnestExtendedPrismaClient (48-48)
libnest.config.ts (1)
  • UserConfig (6-6)
src/modules/auth/auth.config.ts (1)
  • Permission (102-102)
🪛 GitHub Check: ci
example/app.ts

[failure] 3-3:
Module '"@prisma/client"' has no exported member 'PrismaClient'.

🪛 GitHub Actions: Main
example/app.ts

[error] 3-3: TypeScript error TS2305: Module '@prisma/client' has no exported member 'PrismaClient'.

🪛 Biome (1.9.4)
src/modules/auth/auth.module.ts

[error] 68-68: Using super in a static context can be confusing.

super refers to a parent class.
Unsafe fix: Use the class name instead.

(lint/complexity/noThisInStatic)

src/user-config.ts

[error] 68-68: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 71-71: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)


[error] 75-75: An empty interface is equivalent to {}.

Safe fix: Use a type alias instead.

(lint/suspicious/noEmptyInterface)

🔇 Additional comments (2)
src/modules/auth/__tests__/auth.module.spec.ts (1)

46-46: LGTM!

Type simplification aligns with the auth module refactor.

example/app.ts (1)

3-3:

Details

❌ Incorrect review comment

Fix the PrismaClient import - module export not found

The import fails because PrismaClient is not exported from @prisma/client. This is preventing compilation.


🏁 Script executed:

#!/bin/bash
# Check if PrismaClient is generated and find correct import path
fd -e ts -e js "PrismaClient" --exec grep -l "export.*PrismaClient" {}

Length of output: 72


🌐 Web query:

How to properly import PrismaClient from @prisma/client in TypeScript?

💡 Result:

To properly import PrismaClient from @prisma/client in TypeScript, you need to follow a few steps to ensure the client is correctly set up and imported in your project.

Installation Process

First, you need to install the necessary packages:

  1. Install Prisma CLI as a development dependency:

    npm install prisma --save-dev
  2. Install the Prisma Client package:

    npm install @prisma/client

Generating Prisma Client

After installation, you need to generate the Prisma Client based on your schema:

npx prisma generate

This command reads your Prisma schema and generates TypeScript types and methods tailored to your database models[3][4]. The generated client will reflect your schema's structure, enabling type-safe database queries.

Importing PrismaClient

There are multiple ways to import PrismaClient in your TypeScript application:

Standard Import Method

import { PrismaClient } from '@prisma/client'

const prisma = new PrismaClient()
// Use prisma in your application to read and write data in your DB

This is the most common approach and works for most TypeScript projects[1][3].

Custom Output Location

If you've specified a custom output path in your schema, you would import from that location instead:

import { PrismaClient } from './generated/client'

const prisma = new PrismaClient()

To specify a custom output location, update your schema.prisma file[5]:

generator client {
  provider = "prisma-client-js"
  output   = "../src/generated/client"
}

Edge Environment Import

For edge environments, you can import Prisma Client as follows:

import { PrismaClient } from '@prisma/client/edge'

const prisma = new PrismaClient()

Or if using a custom output path:

import { PrismaClient } from './generated/prisma/edge'

const prisma = new PrismaClient()

Using PrismaClient in a Service

In a service-oriented architecture, you might create a dedicated service for Prisma:

// prisma.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient {
  constructor() {
    super();
  }
}

// users.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { User, Prisma } from '@prisma/client';

@Injectable()
export class UsersService {
  constructor(private prisma: PrismaService) {}

  async user(userWhereUniqueInput: Prisma.UserWhereUniqueInput): Promise<User | null> {
    return this.prisma.user.findUnique({
      where: userWhereUniqueInput,
    });
  }
}

This approach is commonly used in NestJS applications[8].

Important Notes

  1. Whenever you make changes to your Prisma schema, you need to run prisma generate again to update the generated client[3][4].

  2. The generated types ensure all your database queries are type-safe, providing excellent autocomplete experience[6].

  3. If you're using ECMAScript modules (ESM), you might need special configuration. The default Node.js environment doesn't support import statements natively without additional setup[2].

Following these steps will ensure you properly import and use PrismaClient in your TypeScript project while benefiting from Prisma's type safety features.

Citations:


Incorrect PrismaClient import assessment

The import import { PrismaClient } from '@prisma/client' is in fact the correct syntax. The missing export you observed comes from not having generated the client into node_modules. To resolve:

  • Ensure you’ve run npm install @prisma/client.
  • Run npx prisma generate after any schema changes.
  • No changes to example/app.ts are needed—keep using:
    import { PrismaClient } from '@prisma/client';
    const prisma = new PrismaClient();

Likely an incorrect or invalid review comment.

🧰 Tools
🪛 GitHub Check: ci

[failure] 3-3:
Module '"@prisma/client"' has no exported member 'PrismaClient'.

🪛 GitHub Actions: Main

[error] 3-3: TypeScript error TS2305: Module '@prisma/client' has no exported member 'PrismaClient'.

new ConfigurableModuleBuilder<AuthModuleOptions<any, any, any>>()
new ConfigurableModuleBuilder<AuthModuleOptions<any>>()
.setClassMethodName('forRoot')
.setExtras({}, (definition) => ({
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid using any type - reduces type safety

Replace any with a proper type constraint to maintain type safety.

-new ConfigurableModuleBuilder<AuthModuleOptions<any>>()
+new ConfigurableModuleBuilder<AuthModuleOptions>()

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/modules/auth/auth.config.ts at line 79, the use of the `any` type in
`AuthModuleOptions<any>` reduces type safety. Replace `any` with a specific type
or a generic type parameter that accurately reflects the expected type
constraints for `AuthModuleOptions` to ensure stronger type checking and
maintain type safety.

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)
src/mixins/__tests__/data-transfer-object.mixin.test.ts (1)

2-2: 💡 Verification agent

🧩 Analysis chain

Check the zod/v4 import path

'zod/v4' is not published on npm at the time of writing (current package name is simply zod). If this repo isn’t vendoring a fork, the import will break CI and downstream builds.

Run a quick search to confirm no local alias is configured:


🏁 Script executed:

#!/bin/bash
# Look for path-based mocks or aliases of "zod/v4".
rg --fixed-strings "zod/v4" -n

Length of output: 1243


🏁 Script executed:

#!/bin/bash
echo "=== tsconfig files and path mappings ==="
find . -maxdepth 4 -type f -name 'tsconfig*.json' -print | while read file; do
  echo "File: $file"
  rg '"paths"' -n "$file" -A5 || echo "  No 'paths' key in $file"
done

echo "=== package.json imports ==="
if [ -f package.json ]; then
  rg '"imports"' -n package.json -A10 || echo "No 'imports' key in package.json"
fi

echo "=== webpack config alias ==="
find . -maxdepth 4 -type f -name 'webpack.config*.js' -print | while read file; do
  echo "File: $file"
  rg 'alias' -n "$file" -A5 || echo "  No 'alias' found in $file"
done

echo "=== vite config alias ==="
find . -maxdepth 4 -type f -name 'vite.config*.ts' -print | while read file; do
  echo "File: $file"
  rg 'alias' -n "$file" -A5 || echo "  No 'alias' found in $file"
done

echo "=== jest config moduleNameMapper ==="
find . -maxdepth 4 -type f -name 'jest.config*' -print | while read file; do
  echo "File: $file"
  rg 'moduleNameMapper' -n "$file" -A5 || echo "  No 'moduleNameMapper' found in $file"
done

Length of output: 1210


Incorrect zod/v4 import (no alias configured)

Our search of tsconfig, package.json, webpack/vite and Jest configs found no path or alias for "zod/v4". The published package is just zod, so this import will break CI.

  • In src/mixins/__tests__/data-transfer-object.mixin.test.ts (and elsewhere) replace:

    - import { z } from 'zod/v4';
    + import { z } from 'zod';

You should update all occurrences of "zod/v4" in the codebase to "zod".

🧹 Nitpick comments (1)
src/mixins/__tests__/data-transfer-object.mixin.test.ts (1)

22-24: Good edge-case test – minor robustness nit

Nice addition to ensure anonymous DTOs keep an empty name. Consider using a length check for a bit more resiliency across runtimes/minifiers:

-    expect(DataTransferObject({}).name).toBe('');
+    expect(DataTransferObject({}).name.length).toBe(0);
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 782f293 and 844e2ef.

📒 Files selected for processing (2)
  • src/mixins/__tests__/data-transfer-object.mixin.test.ts (1 hunks)
  • src/mixins/data-transfer-object.mixin.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/mixins/data-transfer-object.mixin.ts

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

♻️ Duplicate comments (1)
src/utils/validation.utils.ts (1)

48-50: Unchecked non-null assertion can explode at runtime

schema.properties[key]! blindly assumes the property exists. If a Zod object is defined with strict() or properties are pruned elsewhere, this may be undefined, causing a throw later. Add a guard:

-  properties[key] = getSwaggerPropertyMetadata(schema.properties[key]!);
+  const propSchema = schema.properties[key];
+  if (propSchema) {
+    properties[key] = getSwaggerPropertyMetadata(propSchema);
+  }
🧹 Nitpick comments (1)
src/utils/validation.utils.ts (1)

1-4: Deep-linking into @nestjs/swagger/dist/* is fragile

Importing internal paths (dist/decorators/..., dist/interfaces/...) couples you to Nest’s private file layout and may break on any minor upgrade. Prefer the public surface:

-import { createApiPropertyDecorator } from '@nestjs/swagger/dist/decorators/api-property.decorator.js';
-import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js';
+import { ApiProperty } from '@nestjs/swagger';
+import type { SchemaObject } from '@nestjs/swagger';

You can replicate createApiPropertyDecorator behaviour with ApiProperty (or wrap it) while keeping the dependency stable.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 3f9ef5d and cc239a9.

📒 Files selected for processing (2)
  • src/utils/__tests__/validation.utils.test.ts (1 hunks)
  • src/utils/validation.utils.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/utils/__tests__/validation.utils.test.ts (1)
src/utils/validation.utils.ts (3)
  • getSwaggerPropertyMetadata (20-72)
  • getJsonSchemaForSwagger (12-18)
  • applySwaggerMetadata (74-86)

Comment on lines +37 to +45
it('should correctly handle integers', () => {
expectSwaggerMetadata({
input: z.int(),
output: {
type: 'integer'
}
});
expectSwaggerMetadata({
input: z.number().int(),
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

z.int() does not exist – replace with z.number().int()

The Zod API exposes .int() as a refinement on z.number(), not as a top-level constructor. z.int() will throw at runtime (and won’t type-check).

-      input: z.int(),
+      input: z.number().int(),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
it('should correctly handle integers', () => {
expectSwaggerMetadata({
input: z.int(),
output: {
type: 'integer'
}
});
expectSwaggerMetadata({
input: z.number().int(),
it('should correctly handle integers', () => {
expectSwaggerMetadata({
input: z.number().int(),
output: {
type: 'integer'
}
});
expectSwaggerMetadata({
input: z.number().int(),
🤖 Prompt for AI Agents
In src/utils/__tests__/validation.utils.test.ts between lines 37 and 45, replace
all instances of z.int() with z.number().int() because z.int() is not a valid
Zod constructor and will cause runtime errors. Update the test cases to use
z.number().int() to correctly represent integer validation according to the Zod
API.

Comment on lines +82 to +85
for (const propertyKey in baseSchema.properties) {
const propertyMetadata = getSwaggerPropertyMetadata(baseSchema.properties[propertyKey]!);
createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey);
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

baseSchema.properties may be undefined

for (const propertyKey in baseSchema.properties) presumes the root object defines properties. When a Zod object is declared with no fields (e.g. z.object({})) this is undefined, leading to TypeError: Cannot convert undefined or null to object.

-if (baseSchema.type === 'object') {
-  for (const propertyKey in baseSchema.properties) {
+if (baseSchema.type === 'object' && baseSchema.properties) {
+  for (const propertyKey in baseSchema.properties) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for (const propertyKey in baseSchema.properties) {
const propertyMetadata = getSwaggerPropertyMetadata(baseSchema.properties[propertyKey]!);
createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey);
}
if (baseSchema.type === 'object' && baseSchema.properties) {
for (const propertyKey in baseSchema.properties) {
const propertyMetadata = getSwaggerPropertyMetadata(baseSchema.properties[propertyKey]!);
createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey);
}
}
🤖 Prompt for AI Agents
In src/utils/validation.utils.ts around lines 82 to 85, the code assumes
baseSchema.properties is always defined, but it can be undefined for empty Zod
objects, causing a TypeError. Fix this by adding a check to ensure
baseSchema.properties exists before iterating over it, such as using a
conditional or defaulting to an empty object to safely handle cases where
properties is undefined.

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 (3)
src/utils/__tests__/validation.utils.test.ts (1)

51-59: ⚠️ Potential issue

z.int() is invalid – use z.number().int()

Same issue flagged previously: .int() is a refinement on z.number(), not a standalone constructor.
The current call will throw at runtime and fail the test.

-      input: z.int(),
+      input: z.number().int(),
src/utils/validation.utils.ts (2)

48-50: 🛠️ Refactor suggestion

Null-assertion on schema.properties[key] can crash

schema.properties may be undefined (e.g., z.object({})).
Iterating without a guard and using ! risks TypeError: Cannot read properties of undefined.

-      for (const key in schema.properties) {
-        properties[key] = getSwaggerPropertyMetadata(schema.properties[key]!);
+      if (schema.properties) {
+        for (const key in schema.properties) {
+          const child = schema.properties[key];
+          if (child) {
+            properties[key] = getSwaggerPropertyMetadata(child);
+          }
+        }
+      }

83-85: 🛠️ Refactor suggestion

Same undefined-property issue in applySwaggerMetadata

baseSchema.properties is accessed without checking for undefined.
Guard it to avoid crashes on empty objects.

-  for (const propertyKey in baseSchema.properties) {
-    const propertyMetadata = getSwaggerPropertyMetadata(baseSchema.properties[propertyKey]!);
+  const props = baseSchema.properties ?? {};
+  for (const propertyKey in props) {
+    const propertySchema = props[propertyKey];
+    if (!propertySchema) continue;
+    const propertyMetadata = getSwaggerPropertyMetadata(propertySchema);
     createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey);
   }
🧹 Nitpick comments (1)
src/utils/validation.utils.ts (1)

3-4: Avoid relying on internal Swagger paths

@nestjs/swagger/dist/decorators/api-property.decorator.js is an internal file and not part of the public API — its path may change in any minor release. Prefer the public helper (ApiProperty) or re-export a stable wrapper in your own codebase.

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between cc239a9 and 1c5ba88.

📒 Files selected for processing (2)
  • src/utils/__tests__/validation.utils.test.ts (1 hunks)
  • src/utils/validation.utils.ts (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/utils/__tests__/validation.utils.test.ts (1)
src/utils/validation.utils.ts (3)
  • getSwaggerPropertyMetadata (20-73)
  • getJsonSchemaForSwagger (12-18)
  • applySwaggerMetadata (75-87)
🔇 Additional comments (1)
src/utils/validation.utils.ts (1)

12-18: Confirm availability of z.toJSONSchema

toJSONSchema is not included in vanilla Zod ≥3.22; it normally comes from the separate zod-to-json-schema package. Ensure the correct package is installed and that tree-shaking doesn’t drop the function in production builds.

Comment on lines +1 to +3
import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Missing reflect-metadata import – tests will crash at runtime

Reflect.getMetadata* helpers are provided by the reflect-metadata polyfill, not by Node itself.
Without import 'reflect-metadata'; executed once before these calls, the test suite will throw TypeError: Reflect.getMetadata is not a function.

+import 'reflect-metadata';
 import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
 import { ApiProperty } from '@nestjs/swagger';
 import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js';
import 'reflect-metadata';
import { BadRequestException, InternalServerErrorException } from '@nestjs/common';
import { ApiProperty } from '@nestjs/swagger';
import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js';
🤖 Prompt for AI Agents
In src/utils/__tests__/validation.utils.test.ts at the beginning of the file
(lines 1 to 3), add the import statement `import 'reflect-metadata';` before any
other imports. This ensures the reflect-metadata polyfill is loaded so that
Reflect.getMetadata functions are available during test execution, preventing
runtime TypeErrors.

@joshunrau joshunrau closed this Jun 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant