Conversation
BREAKING CHANGE: Prisma options in app config have changed. See example for more details.
|
Caution Review failedThe pull request is closed. WalkthroughThis 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
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
Possibly related PRs
Suggested labels
Poem
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (2)
✨ Finishing Touches
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed 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)
Other keywords and placeholders
CodeRabbit Configuration File (
|
Codecov ReportAll modified and coverable lines are covered by tests ✅
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. 🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🔭 Outside diff range comments (1)
example/cats/cats.service.ts (1)
15-16:⚠️ Potential issue
crypto.randomUUID()is used without importingnode:crypto.
randomUUIDis not available on the globalcryptoobject in every supported Node LTS release. Importcryptoexplicitly to avoid runtime failures on older runtimes and to satisfy TS type-checking whendomlibs 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
baseUrlis hard-coded a second time rather than re-using the earlier variable orgetVar('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
defineExtensioncurrently returns its argument untyped (unknown). Returningargdirectly works, but exportingvi.fn<Parameters<typeof Prisma.defineExtension>[0], ReturnType<typeof Prisma.defineExtension>>()(or at leastany) 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/clientsize tax in the browser bundleBecause 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/clientwill 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 newPrismaClientLikeabstraction.src/mixins/data-transfer-object.mixin.ts (1)
4-5: Duplication withValidationSchemadecorator.Both this mixin and
ValidationSchemanow callapplyValidationSchemaandapplySwaggerMetadata. 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
applyValidationSchemaorapplySwaggerMetadata, 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 narrowingPrismaClientLike
PrismaClientLikeis essentiallyPrismaClient & { [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 remainprivate readonly
Nothing else in the class mutatesclient; marking itprivate readonlyboth 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 leakingany– 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 tounknown):-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 forRecord<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/noBannedTypescomment 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 instantiatingAppContainer: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
typefield. 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
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis 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 liveRemoving
/v1is 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: Runprisma generateafter bumping to 6.9.x
@prisma/engines,@prisma/get-platform, and@prisma/clientmust stay in lock-step with the generated client. Please run:pnpm prisma:generateand 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 goodPassing the constructor via
prisma.clientcleanly decouples instantiation and is consistent with the new module API.src/modules/prisma/__tests__/prisma.module.test.ts (2)
3-4: Ensure@prisma/clientis mocked before this importThe assertion later depends on
__isMockPrismaClient, which the real Prisma client obviously lacks. Make sure the Vitest module-alias that points@prisma/client→src/testing/mocks/prisma.module.mock.tsexecutes before this line; otherwise the test will silently instantiate the real client and the assertion will fail.
34-37: Constructor injection test update LGTMThe 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
existsmethod 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
unknowncould 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
thiskeyword 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
thiswhen 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 TThis 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, andExactthat 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
thiskeyword[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:
- 1: prisma/prisma#25476
- 2: https://www.prisma.io/docs/orm/prisma-client/type-safety
- 3: prisma/prisma#16974
- 4: https://www.prisma.io/docs/orm/prisma-client/client-extensions/type-utilities
- 5: https://www.prisma.io/docs/orm/prisma-client/type-safety/operating-against-partial-structures-of-model-types
- 6: https://www.prisma.io/docs/orm/prisma-client/client-extensions/query
- 7: prisma/prisma#18628
- 8: https://dev.to/logrocket/end-to-end-type-safety-with-nextjs-prisma-and-graphql-1bgp
- 9: https://dev.to/zenstack/what-prisma-doesnt-have-yet-pan
- 10: https://echobind.com/post/extending-types-for-prisma-extensions-in-nextjs
Prisma extension context typing requires type casting.
Due to TypeScript’s limitations around generics and
thisinference, there isn’t a fully type-safe way to callPrisma.getExtensionContext(this)without a cast. The currentas 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
existsimplementation 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 forMergeExtArgs.🧰 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.
| import { PrismaClient } from '@prisma/client'; | ||
| import request from 'supertest'; |
There was a problem hiding this comment.
🛠️ 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.
| 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); | ||
| } |
There was a problem hiding this comment.
💡 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.
| 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.
| 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); | ||
| } | ||
| } |
There was a problem hiding this comment.
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.
| const result = {} as LibnestPrismaExtensionArgs['result']; | ||
| Object.keys(Prisma.ModelName).forEach((modelName) => { | ||
| result[getModelKey(modelName)] = { | ||
| __modelName: { | ||
| compute: (): string => modelName | ||
| } | ||
| }; | ||
| }); |
There was a problem hiding this comment.
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.
| 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.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (2)
src/modules/prisma/__tests__/prisma.types.test.ts (1)
12-20: Consider replacing{}withRecord<string, never>to silence Biome’snoBannedTypesruleThe banned-type linter fires six times here.
If project policy enforces that rule, swapping toRecord<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 inMergeExtArgstriggers lint – useRecord<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
📒 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 thatPrisma.Extensionis the intended genericPrisma’s public API currently exposes
Prisma.ExtensionArgs;Prisma.Extensionis 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
InferExtendedClientneatly composes the extension, type map callback and ext-args – concise and reusable. 👍
| $allModels: { | ||
| readonly exists: () => <T>(this: T, where: Prisma.Args<T, 'findFirst'>['where']) => Promise<boolean>; | ||
| }; |
There was a problem hiding this comment.
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'; | |||
There was a problem hiding this comment.
🛠️ 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.Locales
BREAKING CHANGE: Use UserTypes to declare JwtPayload and UserQueryMetadata
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (3)
example/app.ts (1)
23-24: Consider tracking this type safety issueThe 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 aliasesEmpty 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 contextUsing
superin 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
📒 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
PrismaClientis 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:
Install Prisma CLI as a development dependency:
npm install prisma --save-devInstall the Prisma Client package:
npm install @prisma/clientGenerating Prisma Client
After installation, you need to generate the Prisma Client based on your schema:
npx prisma generateThis 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 DBThis 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
Whenever you make changes to your Prisma schema, you need to run
prisma generateagain to update the generated client[3][4].The generated types ensure all your database queries are type-safe, providing excellent autocomplete experience[6].
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:
- 1: https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/introduction
- 2: prisma/prisma#2222
- 3: https://www.prisma.io/docs/getting-started/setup-prisma/start-from-scratch/relational-databases/install-prisma-client-typescript-postgresql
- 4: https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/install-prisma-client-typescript-postgresql
- 5: https://www.prisma.io/docs/orm/prisma-client/setup-and-configuration/generating-prisma-client
- 6: https://www.prisma.io/typescript
- 7: https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases/install-prisma-client-typescript-sqlserver
- 8: https://docs.nestjs.com/recipes/prisma
- 9: prisma/prisma#14112
- 10: https://dev.to/stuarthaas/client-side-prisma-types-mj
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 intonode_modules. To resolve:
- Ensure you’ve run
npm install @prisma/client.- Run
npx prisma generateafter any schema changes.- No changes to
example/app.tsare 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) => ({ |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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/v4import path
'zod/v4'is not published on npm at the time of writing (current package name is simplyzod). 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" -nLength 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" doneLength of output: 1210
Incorrect
zod/v4import (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 justzod, 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 nitNice 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
📒 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
There was a problem hiding this comment.
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 withstrict()or properties are pruned elsewhere, this may beundefined, 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 fragileImporting 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
createApiPropertyDecoratorbehaviour withApiProperty(or wrap it) while keeping the dependency stable.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 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)
| it('should correctly handle integers', () => { | ||
| expectSwaggerMetadata({ | ||
| input: z.int(), | ||
| output: { | ||
| type: 'integer' | ||
| } | ||
| }); | ||
| expectSwaggerMetadata({ | ||
| input: z.number().int(), |
There was a problem hiding this comment.
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.
| 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.
| for (const propertyKey in baseSchema.properties) { | ||
| const propertyMetadata = getSwaggerPropertyMetadata(baseSchema.properties[propertyKey]!); | ||
| createApiPropertyDecorator(propertyMetadata, true)(target.prototype, propertyKey); | ||
| } |
There was a problem hiding this comment.
🛠️ 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.
| 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.
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
src/utils/__tests__/validation.utils.test.ts (1)
51-59:⚠️ Potential issue
z.int()is invalid – usez.number().int()Same issue flagged previously:
.int()is a refinement onz.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 suggestionNull-assertion on
schema.properties[key]can crash
schema.propertiesmay beundefined(e.g.,z.object({})).
Iterating without a guard and using!risksTypeError: 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 suggestionSame undefined-property issue in
applySwaggerMetadata
baseSchema.propertiesis accessed without checking forundefined.
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.jsis 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
📒 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 ofz.toJSONSchema
toJSONSchemais not included in vanilla Zod ≥3.22; it normally comes from the separatezod-to-json-schemapackage. Ensure the correct package is installed and that tree-shaking doesn’t drop the function in production builds.
| import { BadRequestException, InternalServerErrorException } from '@nestjs/common'; | ||
| import { ApiProperty } from '@nestjs/swagger'; | ||
| import type { SchemaObject } from '@nestjs/swagger/dist/interfaces/open-api-spec.interface.js'; |
There was a problem hiding this comment.
🛠️ 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.
| 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.
Summary by CodeRabbit
New Features
existsmethod to all models and exposing model name metadata.Improvements
Bug Fixes
Tests
Refactor
Chores