@nest-extended/prisma
The Prisma adapter: the same generic CRUD service and query language as the other adapters, backed by Prisma for PostgreSQL, MySQL and SQLite.
$npm install @nest-extended/core @nest-extended/prisma @nest-extended/decorators nestjs-cls
Key exports
| Export | Type | Purpose |
|---|---|---|
NestService<T, E> | class | CRUD service — _find, _get, _create, _patch, _remove, and their event-firing find / get / create / patch / remove counterparts |
applyFilters() | function | Apply parsed filters to Prisma query options |
rawQuery() | function | Convert a FeathersJS query to a Prisma where |
GlobalExceptionFilter | filter | Maps PrismaClientKnownRequestError (P2002, P2003, P2025, …) to HTTP |
Usage
import { Injectable } from '@nestjs/common';
import { NestService } from '@nest-extended/prisma';
import { PrismaService } from '../prisma/prisma.service';
@Injectable()
export class CatsService extends NestService<any> {
constructor(private readonly prisma: PrismaService) {
super(prisma.cat);
}
}
super()takes the model delegate (prisma.cat), whilethis.prismastays on the instance — that is your route to the rest of the client, including raw SQL. See Raw SQL and aggregates.
await catsService.find({
name: { $iLike: 'kitty' },
age: { $gt: 5 },
$include: { owner: true },
$sort: { createdAt: -1 },
$limit: 10,
});Relations are eager-loaded with
$include(Prisma'sinclude).$iLikeis fully case-insensitive on PostgreSQL — see Querying for per-database notes.
Service events
Since 1.5.0 this service also exposes find / get / create / patch / remove — the
same operations, but they dispatch to an attached events class. Name it as the last generic
to have emit() type-checked:
export class CatsService extends NestService<Cat, CatsEvents> {}See Service Events.
Raw SQL and aggregates
find covers filtering, pagination and relations. Anything beyond it — GROUP BY,
window functions, CTEs, database-specific SQL — goes through the PrismaService you
already injected.
Typed aggregates, no SQL
Reach for these first: they stay type-safe and portable across PostgreSQL, MySQL and SQLite.
async statsByBreed() {
return this.prisma.cat.groupBy({
by: ['breed'],
where: { deleted: false },
_avg: { age: true },
_count: { _all: true },
orderBy: { _avg: { age: 'desc' } },
});
}
async ageRange() {
return this.prisma.cat.aggregate({
where: { deleted: false },
_min: { age: true },
_max: { age: true },
});
}$queryRaw — rows back
Use the tagged template form. Interpolated values become bound parameters, so
${breed} is never spliced into the SQL string.
async topBreeds(minAge: number) {
return this.prisma.$queryRaw<{ breed: string; avgAge: number; total: bigint }[]>`
SELECT breed,
AVG(age)::float AS "avgAge",
COUNT(*) AS total
FROM "Cat"
WHERE deleted IS NOT TRUE
AND age >= ${minAge}
GROUP BY breed
ORDER BY "avgAge" DESC
`;
}$executeRaw — row count back
async retireOldCats(age: number) {
return this.prisma.$executeRaw`
UPDATE "Cat" SET retired = true WHERE age >= ${age} AND deleted IS NOT TRUE
`;
}Wrap several statements with this.prisma.$transaction([...]).
Three things the adapter stops doing
Raw access bypasses the wrapper entirely, so it is on you to:
- Filter soft-deleted rows.
deletedis nullable, sodeleted IS NOT TRUE(notdeleted = false) is the faithful equivalent of the{ deleted: { $ne: true } }filter the adapter merges in — see Soft Delete & Auditing. - Fire events. Raw SQL is not
findorpatch, so no hook runs. Callthis.emit('retireOldCats', count)yourself if listeners should know — see Service Events. - Shape the response. You get raw rows, not the
{ total, $limit, $skip, data }envelope — andCOUNT(*)comes back as aBigInt, whichJSON.stringifyrefuses. Cast it (Number(row.total)) before returning it from a controller.
Avoid
$queryRawUnsafe/$executeRawUnsafe. They take a plain string and do not parameterise it, so any user-supplied value is an injection. If you need a dynamic identifier such as a column name, validate it against an allow-list first.Identifier quoting is database-specific:
"Cat"for PostgreSQL, backticks for MySQL. The examples above are PostgreSQL.