About this project
# @querry-kit/nest
Consolidated NestJS helpers for Query Kit APIs: fields projection, Prisma-style query services, CASL policies, OpenAPI decorators, pipes, pagination DTOs, and object utilities.
## 🌐 Querry Kit Ecosystem
The [Querry Kit overview](https://querry-kit.github.io/querry-kit/) connects the three core packages:
- [`@querry-kit/nest`](https://github.com/querry-kit/nest) provides the NestJS API and controller patterns.
- [`@querry-kit/nuxt`](https://github.com/querry-kit/nuxt) provides typed API clients and headless Vue/Nuxt data primitives.
- [`@querry-kit/nuxt-ui`](https://github.com/querry-kit/nuxt-ui) provides Nuxt UI table controls built on those primitives.
## 📦 Install
```sh
pnpm add @querry-kit/nest
pnpm add @nestjs/common @nestjs/core @nestjs/swagger class-transformer class-validator reflect-metadata
```
For the optional CASL adapter:
```sh
pnpm add @casl/ability @casl/prisma
```
The current package version is published on npm. npm is the primary distribution channel.
GitHub release tags remain available as a fallback:
```sh
pnpm add github:querry-kit/nest#v0.0.1
```
## 🚀 Release Workflow
Releases are driven by Changesets and GitHub Actions. The `main` branch does not contain committed `dist` files; it only contains source, the README, and workflow configuration.
Package-visible changes should include a changeset:
```sh
pnpm changeset
```
When changes land on `main`, the `changesets` workflow creates or updates a release PR. That PR contains the version bump and changelog updates produced by:
```sh
pnpm changeset version
```
The 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:
```sh
pnpm add @querry-kit/nest
```
## 🧩 Usage
`ResourceQuery` 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.
```ts
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:
```ts
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:
```ts
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
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.
```ts
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:
```ts
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
`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`.
```txt
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:
```ts
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),
};
```
## 📖 Documentation
- [Getting Started](https://querry-kit.github.io/querry-kit/docs/nest/guide/getting-started)
- [Complete API Example](https://querry-kit.github.io/querry-kit/docs/nest/guide/example-app)
- [CRUD Controller](https://querry-kit.github.io/querry-kit/docs/nest/guide/crud-controller)
- [Fields](https://querry-kit.github.io/querry-kit/docs/nest/api/fields/)
- [Query Service](https://querry-kit.github.io/querry-kit/docs/nest/api/query-service)
- [DTOs and Pagination](https://querry-kit.github.io/querry-kit/docs/nest/api/dtos-pagination)
- [OpenAPI Decorators](https://querry-kit.github.io/querry-kit/docs/nest/api/openapi)
- [CASL](https://querry-kit.github.io/querry-kit/docs/nest/api/casl)
- [API Reference](https://querry-kit.github.io/querry-kit/docs/nest/api/)
## 🛠 Development
```sh
pnpm install
pnpm lint
pnpm check
pnpm test
pnpm test:coverage
pnpm build
```
The workspace build allowlist permits the native scripts required by the test and documentation toolchain, including Parcel's file watcher.
`pnpm 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.
Comments
0 Rating appears after 10 ratings
Sign in to join the discussion.