ServicePrisma - muzyk0/crud GitHub Wiki
@nestjsx/crud-prisma is the Prisma service adapter for @nestjsx/crud.
It is not a reflection-based drop-in replacement for @nestjsx/crud-typeorm. The adapter keeps controllers, request parsing, route generation, query parsing, and CrudAuth() behavior in place while moving ORM-specific behavior into explicit Prisma model config.
npm i @nestjsx/crud-prisma @prisma/client prismaEvery PrismaCrudService needs:
modelNamescalarFields-
stringFieldswhenfilterororquery params should stay string-typed for Prisma string operators primaryKeyswhereUnique(params, entity)
Optional config:
-
relationMapforjoin, nestedjoin, eager relations, required joins, and nested sorting -
softDeletefordeleteOne()andrecoverOne() -
write.normalizeCreate,write.normalizeUpdate,write.normalizeReplace,write.normalizeDelete, andwrite.normalizeRecoverfor explicit write shaping
import { Injectable } from '@nestjs/common';
import { PrismaCrudService, definePrismaCrudModelConfig } from '@nestjsx/crud-prisma';
import { PrismaService } from '../prisma/prisma.service';
import { Company } from './company.model';
const companyModel = definePrismaCrudModelConfig<Company>({
modelName: 'Company',
scalarFields: ['id', 'name', 'domain', 'description', 'deletedAt'],
stringFields: ['name', 'domain', 'description'],
primaryKeys: ['id'],
softDelete: {
field: 'deletedAt',
deletedValue: () => new Date(),
notDeletedValue: null,
},
whereUnique: (params, entity) => ({
id: Number(entity && entity.id ? entity.id : params.id),
}),
});
@Injectable()
export class CompaniesService extends PrismaCrudService<Company> {
constructor(prisma: PrismaService) {
super(prisma.company, {
model: companyModel,
query: {
softDelete: true,
},
});
}
}import { Controller } from '@nestjs/common';
import { Crud } from '@nestjsx/crud';
import { Company } from './company.model';
import { CompaniesService } from './companies.service';
@Crud({
model: {
type: Company,
},
query: {
softDelete: true,
},
})
@Controller('companies')
export class CompaniesController {
constructor(public service: CompaniesService) {}
}The integration fixture keeps a nested controller route and Prisma-backed service:
@Crud({
model: {
type: User,
},
params: {
companyId: {
field: 'companyId',
type: 'number',
},
id: {
field: 'id',
type: 'number',
primary: true,
},
},
routes: {
deleteOneBase: {
returnDeleted: true,
},
},
query: {
softDelete: true,
join: {
company: {
exclude: ['description'],
},
'company.projects': {
exclude: ['description'],
},
profile: {
eager: true,
},
},
},
})
@Controller('/companies/:companyId/users')
export class UsersController {
constructor(public service: UsersService) {}
}The corresponding Prisma model config uses relationMap, stringFields, softDelete, and whereUnique to make the service behavior explicit. allowParamsOverride stays false unless a route opts in, so route params continue to win over payload values on endpoints such as POST /companies/:companyId/users and PATCH /companies/:companyId/users/:id.
- Keep the existing
@Crud()controller. - Replace
TypeOrmCrudService<Entity>withPrismaCrudService<Model>. - Convert entity metadata into
definePrismaCrudModelConfig(). - Add
softDeleteandwhereUniqueexplicitly instead of relying on decorators or repository metadata. - Add
write.normalize*hooks only where nested writes or DTO normalization need to be explicit.
This migration strategy lets you move one controller/service pair at a time.
The Prisma integration fixtures and tests currently verify:
GET /companies?include_deleted=1GET /users/1?join=company&join=company.projectsPOST /companies/:companyId/usersPATCH /mePOST /projects
Those fixtures show that include_deleted, eager joins, required joins, returnDeleted, and CrudAuth.persist() continue to work through the Prisma adapter when the model config declares the needed metadata.
Supported:
- Scalar field selection through scalarFields.
- Relation traversal through relationMap, including nested relation metadata.
- Compound primary keys and soft delete through explicit model configuration.
- Write normalization through optional create, update, replace, delete, and recover hooks.
Known non-goals:
- TypeORM runtime metadata parity or inferred relation discovery.
- Implicit nested writes or cascade behavior.
- Transparent query-cache parity without an explicit extension hook.
Additional notes:
- Request-level cache settings are a no-op by default and only become active when PrismaCrudOptions.cache is provided.
- When PrismaCrudOptions.cache is enabled, provide
get(key)and/orset(key, value, ttl)so the adapter can wrap normalized Prisma args with request-scoped cache reads and writes. - Mutation lookups and post-write refetches bypass that cache so write paths do not reuse stale read results.
- Declare
stringFieldson root and relation config anywhere legacyfilterororvalues such as5must remain"5"for Prisma string comparisons, ranges, membership checks, or case-insensitive operators. - Join aliases remain compatibility metadata only and do not drive Prisma query generation.
- Soft delete behavior must be declared through softDelete config rather than ORM-specific decorators.
-
returnShallowis honored for create, update, and replace routes,returnDeletedis honored for delete routes, andreturnRecoveredis honored for recover routes. WhenreturnShallowis false, create, update, and replace refetch the entity inside the current request scope when they can resolve a primary-key lookup. Delete and recover return a body only when their route flag is enabled.