Consolidated NestJS helpers for Query Kit APIs: fields projection, Prisma-style query services, CASL policies, OpenAPI decorators, pipes, pagination DTOs, and object utilities.
π Documentation: https://querry-kit.github.io/nest/
The Querry Kit overview connects the three core packages and the Workboard reference application:
@querry-kit/nestprovides the NestJS API and controller patterns.@querry-kit/nuxtprovides typed API clients and headless Vue/Nuxt data primitives.@querry-kit/nuxt-uiprovides Nuxt UI table controls built on those primitives.
- π Querry Kit Ecosystem
- π¦ Install
- π Release Workflow
- π§© Usage
- π CASL
- π― Fields
- π§ͺ Example API
- π Documentation
- π Development
pnpm add @querry-kit/nest
pnpm add @nestjs/common @nestjs/core @nestjs/swagger class-transformer class-validator reflect-metadataFor the optional CASL adapter:
pnpm add @casl/ability @casl/prismaThe current package version is published on npm. npm is the primary distribution channel.
GitHub release tags remain available as a fallback:
pnpm add github:querry-kit/nest#v0.0.1Releases are driven by Changesets and GitHub Actions. The main branch does not contain committed dist files; it only contains source, documentation, examples, and workflow configuration.
Package-visible changes should include a changeset:
pnpm changesetWhen changes land on main, the changesets workflow creates or updates a release PR. That PR contains the version bump and changelog updates produced by:
pnpm changeset versionThe npm publish workflow uses npm Trusted Publishing through GitHub Actions OIDC. The npm package must be connected to this repository and workflow in the npm package publishing settings:
- Repository:
querry-kit/nest - Workflow file:
release.yml - Environment: unset
After the release PR is merged, the npm publish workflow runs for the version/changelog changes. It runs package checks, builds dist in CI, publishes @querry-kit/nest to npm, tags the release commit as vX.Y.Z, and creates a GitHub Release.
Consumers should install from npm:
pnpm add @querry-kit/nestResourceQuery covers the common controller flow: parse optional fields, merge endpoint-required includes with client include values, generate Prisma includes for selected relations, call a QueryService, map models to DTOs, and project the response.
import { Get, Query, Req } from '@nestjs/common';
import { ApiErrorResponses, ApiPaginatedResponse, ApiResourceQuery, QueryDTO, ResourceQuery } from '@querry-kit/nest';
@Get()
@ApiResourceQuery()
@ApiPaginatedResponse({ model: CustomerDTO })
@ApiErrorResponses({ badRequestDescription: 'Invalid query parameter.' })
async query(@Req() req: AuthRequest, @Query() query: QueryDTO<CustomerTypeMap>) {
return ResourceQuery.query({
service: this.customersService,
query,
schema: CustomerDTO,
ability: req.ability,
map: (customer, ability) => CustomerDTO.fromModel(customer, ability),
});
}Create resource services with QueryService and a Prisma-compatible delegate:
import { Injectable } from '@nestjs/common';
import { QueryService, type BaseDelegateTypeMap } from '@querry-kit/nest';
import { Prisma, PrismaService } from '../prisma';
interface CustomerTypeMap extends BaseDelegateTypeMap {
select: Prisma.CustomerSelect;
include: Prisma.CustomerInclude;
whereInput: Prisma.CustomerWhereInput;
orderByWithRelationInput: Prisma.CustomerOrderByWithRelationInput;
whereUniqueInput: Prisma.CustomerWhereUniqueInput;
scalarFieldEnum: Prisma.CustomerScalarFieldEnum;
}
@Injectable()
export class CustomersService extends QueryService<typeof PrismaService.prototype.customer, CustomerTypeMap> {
constructor(prisma: PrismaService) {
super(prisma.customer);
}
}Focused subpaths are available for smaller imports:
import { createCaslAccessibleWhere, filterCaslFields } from '@querry-kit/nest/casl';
import { ApiResourceQuery } from '@querry-kit/nest/decorators';
import { Fields } from '@querry-kit/nest/fields';
import { parseObject } from '@querry-kit/nest/object';
import { QueryService } from '@querry-kit/nest/query-service';CASL is optional. Pass ability only when the endpoint should merge an authorization-aware where clause through QueryService; omit it for APIs that do not use CASL.
import { createCaslAccessibleWhere } from '@querry-kit/nest/casl';
super(prisma.customer, {
subject: 'Customer',
accessibleWhere: createCaslAccessibleWhere({ action: 'read' }),
});When an ability is passed to query, the service combines the CASL where clause with user filters as { AND: [accessibleWhere, parsedWhere] }.
For field-level response permissions, filter the completed DTO without mutating it:
return filterCaslFields(dto, 'Customer', ability);The helper uses the read action by default. Pass { action: RoleAction.READ } when an application uses uppercase or enum-backed actions. It filters serialized DTO fields only; keep passing the ability to QueryService to constrain database reads too.
fields is optional by default. When omitted, Query Kit returns the complete DTO response. When present, it validates the syntax and selected fields, adds required relation includes, and projects the returned DTOs. Outer braces are optional, so {id,title} is equivalent to id,title.
GET /books?fields=id,title
GET /books?fields={id,title}
GET /books?fields=items{id,title},meta{page,perPage,itemCount,pageCount}
GET /books?fields=items{},meta{page}An explicit empty value (?fields=) or empty outer selection (?fields={}) returns {}. Empty nested selections are also valid: items{} produces empty item objects, while meta{page} keeps only the requested page metadata. A value containing only whitespace remains invalid.
Use prepareFieldsQuery directly when a controller needs custom service orchestration:
import { Fields, prepareFieldsQuery } from '@querry-kit/nest/fields';
const prepared = prepareFieldsQuery({
fields: query.fields,
include: query.include,
schema: BookDTO,
requiredInclude: { author: true },
});
const { items, pageMeta } = await booksService.query({ ...query, include: prepared.include });
return {
items: Fields.project(items.map(BookDTO.fromModel), prepared.projection),
meta: Fields.project(pageMeta, prepared.metaProjection),
};A small in-memory NestJS example lives in examples/books-api. It uses books and authors to show relation includes, fields projection, pagination metadata, CASL-aware controller flow, query transformation, and OpenAPI documentation.
pnpm examples:check
pnpm examples:build- Getting Started
- Example App
- CRUD Controller
- Fields
- Query Service
- DTOs and Pagination
- OpenAPI Decorators
- CASL
- API Reference
Run the VitePress documentation locally:
pnpm docs:devBuild the documentation:
pnpm docs:buildpnpm install
pnpm lint
pnpm check
pnpm test
pnpm test:coverage
pnpm build
pnpm examples:check
pnpm examples:build
pnpm docs:buildpnpm test:coverage collects all source files, prints the coverage summary, and writes HTML and LCOV reports to coverage/. GitHub Actions runs the same command and retains the report as a workflow artifact.