If you build NestJS APIs on top of Prisma, you've probably felt the friction: your schema is the source of truth, but your DTOs, validation rules, and Swagger metadata live somewhere else — and they drift.
Most Prisma DTO generators solve the basics (Create / Update / Entity classes with decorators). That's useful, but the moment you need custom output — audit metadata, GraphQL types, RBAC manifests, your own validators — you're back to hand-written glue code.
@tommasomeli/prisma-generator-nestjs-dto is a Prisma generator built for that second phase. It emits the usual NestJS DTOs, then gets out of your way — with a plugin API, annotation-driven decorators, and a type-safe config file when the Prisma schema block isn't enough.
What you get out of the box
For every Prisma model User, run npx prisma generate and you get:
src/generated/nestjs-dto/
user/
user.entity.ts
create-user.dto.ts
update-user.dto.ts
index.ts
index.ts
Each file ships with:
-
class-validatordecorators (@IsString,@IsOptional, …) -
@nestjs/swaggermetadata (@ApiProperty,@ApiHideProperty, …) -
Self-contained imports — relation DTOs,
@DtoOverrideTypetargets, and annotation arguments are resolved automatically
Configure it directly in schema.prisma:
generator nestjsDto {
provider = "prisma-generator-nestjs-dto"
output = "../src/generated/nestjs-dto"
outputType = "class"
outputStructure = "nestjs"
fileNamingStrategy = "kebab"
reExport = "true"
classValidator = "true"
swaggerDocs = "true"
prettier = "true"
}
Install:
npm i -D @tommasomeli/prisma-generator-nestjs-dto
Annotations — control visibility without touching generated code
Triple-slash comments (///) above models and fields drive the built-in generators. No post-processing, no manual edits.
model User {
id Int @id @default(autoincrement())
email String @unique
name String
/// @DtoHidden
passwordHash String
/// @DtoReadOnly
createdAt DateTime @default(now())
}
| Annotation | Effect |
|---|---|
@DtoHidden |
Hide everywhere |
@DtoReadOnly |
Exclude from Create and Update DTOs |
@DtoEntityHidden |
Hide in the Entity (API response) |
@DtoCreateHidden / @DtoUpdateHidden
|
Hide in one DTO only |
@DtoOverrideType(MyType) |
Override the TypeScript type (auto-imported) |
@DtoIgnoreModel |
Skip the model entirely |
You can also bind your own validators and decorators via config — more on that below.
The plugin system — when built-in DTOs aren't enough
The differentiator is extraGenerators: drop in any class extending BaseGenerator and it runs alongside the built-ins in the same pipeline.
A plugin receives:
- The parsed model graph (with annotations on every field)
- Resolved config (
extraDecorators,extraValidators,extraImports, …) - Import-merging helpers (
addImport,formatImports,getTemplate)
Here's a minimal plugin that reuses the built-in renderer to emit an Entity without class-validator:
import { isEntityHidden } from '@tommasomeli/prisma-generator-nestjs-dto';
import { BaseGenerator } from '@tommasomeli/prisma-generator-nestjs-dto';
import type { Field, File, Model } from '@tommasomeli/prisma-generator-nestjs-dto';
export default class EntityDtoGenerator extends BaseGenerator {
filePrefix = '';
fileSuffix = '.entity';
classPrefix = '';
classSuffix = '';
async generate(): Promise<File[]> {
return this.models.map((model) => {
const filteredFields = model.fields.filter((f: Field) => !isEntityHidden(f));
const processedModel: Model = { ...model, fields: filteredFields as Field[] };
const outputPath = this.getPath(model);
return {
path: outputPath,
content: this.getTemplate({ model: processedModel, classValidator: false, outputPath }),
};
});
}
}
Lifecycle hooks
Plugins can hook the full run:
-
beforeAll(models)— mutate the shared model list before any generator runs (shared pre-pass) -
afterAll(files)— append barrels, audit reports, or aggregated indexes after all generators finish
TypeScript plugins, no precompilation
Point extraGenerators at a .ts file and the generator loads it via jiti. No build step required for your plugin.
Real example: custom @Auditable annotation
The repo includes a runnable example under examples/blog/.
Schema — annotate models with a custom @Auditable name:
/// @Auditable("user_audit")
model User {
id Int @id @default(autoincrement())
email String @unique
/// @DtoHidden
passwordHash String
posts Post[]
}
/// @Auditable("post_audit")
model Post {
id Int @id @default(autoincrement())
title String
author User @relation(fields: [authorId], references: [id])
authorId Int
}
Config — register the annotation and the plugin:
import { from, type GeneratorConfigFile } from '@tommasomeli/prisma-generator-nestjs-dto';
export default {
extraAnnotations: ['Auditable'],
extraGenerators: from('./generators/audit-generator.ts', ['AuditGenerator']),
} satisfies GeneratorConfigFile;
Output — alongside the usual DTOs, you get per-model audit metadata and an aggregated index:
generated/
user/user.audit.ts
post/post.audit.ts
audit-index.ts ← emitted by AuditGenerator#afterAll
Type-safe external config
Prisma's generator block is great for simple flags, but it can't express nested objects or multi-line arrays. For anything richer, use a configFile:
generator nestjsDto {
provider = "prisma-generator-nestjs-dto"
output = "../generated"
configFile = "../nestjs-dto.config.ts"
}
The from() helper validates paths and named exports at compile time (the import closure is never invoked at runtime):
import { from, fromNamespace, type GeneratorConfigFile } from '@tommasomeli/prisma-generator-nestjs-dto';
export default {
extraValidators: from(() => import('src/common/validators'), ['IsUnique', 'IsStrongPassword']),
extraDecorators: from(() => import('src/common/decorators'), ['Trim', 'Sanitize']),
extraScalars: {
Decimal: { ts: 'Decimal', from: 'decimal.js' },
Json: { ts: 'MyJson', from: 'src/json', apiType: 'Object' },
},
} satisfies GeneratorConfigFile;
Then wire annotations in the schema:
model User {
/// @IsUnique()
email String @unique
/// @IsStrongPassword({ minLength: 10 })
password String
/// @Trim()
name String
}
If a custom validator collides by name with a built-in (IsBoolean, ApiProperty, …), your module wins for that symbol only.
Optional runtime manifest
Enable emitManifest = "true" to get:
-
manifest.ts—Record<Prisma.ModelName, { primaryKey, entityFields, relations }>for select builders, audit middleware, RBAC field lists -
model-entity-map.ts— type-only map from model names to Entity classes
Useful when you need schema-aware runtime logic without parsing Prisma DMMF yourself.
How it compares
| This generator | Typical Prisma NestJS DTO generators | |
|---|---|---|
| Create / Update / Entity DTOs | ✅ | ✅ |
| Swagger + class-validator | ✅ | ✅ |
| Annotation-driven hide / readonly / type override | ✅ | partial |
| Pluggable sub-generators | ✅ | ❌ |
| Custom annotations for plugins | ✅ | ❌ |
| Override built-in imports by name | ✅ | ❌ |
Type-safe external configFile
|
✅ | ❌ |
| Optional runtime manifest | ✅ | ❌ |
Try it
npm i -D @tommasomeli/prisma-generator-nestjs-dto
npx prisma generate
- GitHub: github.com/tommasomeli/prisma-generator-nestjs-dto
- npm: @tommasomeli/prisma-generator-nestjs-dto
-
Example:
examples/blog/
Issues and PRs are welcome — MIT licensed.
If this saves you time on your NestJS + Prisma stack, a ⭐ on the repo (or a coffee ☕) goes a long way.
Top comments (0)