프로젝트 소개

# @querry-kit/nest Query Kit API를 위한 통합 NestJS 헬퍼: 필드 프로젝션, Prisma 스타일 쿼리 서비스, CASL 정책, OpenAPI 데코레이터, 파이프, 페이지네이션 DTO, 객체 유틸리티. ## 🌐 Querry Kit 생태계 [Querry Kit 개요](https://querry-kit.github.io/querry-kit/)는 세 가지 핵심 패키지를 연결합니다: - [`@querry-kit/nest`](https://github.com/querry-kit/nest)는 NestJS API 및 컨트롤러 패턴을 제공합니다. - [`@querry-kit/nuxt`](https://github.com/querry-kit/nuxt)는 타입이 지정된 API 클라이언트와 헤드리스 Vue/Nuxt 데이터 프리미티브를 제공합니다. - [`@querry-kit/nuxt-ui`](https://github.com/querry-kit/nuxt-ui)는 이러한 프리미티브를 기반으로 한 Nuxt UI 테이블 컨트롤을 제공합니다. ## 📦 설치 ```sh pnpm add @querry-kit/nest pnpm add @nestjs/common @nestjs/core @nestjs/swagger class-transformer class-validator reflect-metadata ``` 선택적 CASL 어댑터의 경우: ```sh pnpm add @casl/ability @casl/prisma ``` 현재 패키지 버전은 npm에 게시되어 있습니다. npm이 기본 배포 채널입니다. GitHub 릴리스 태그는 대체 수단으로 사용할 수 있습니다: ```sh pnpm add github:querry-kit/nest#v0.0.1 ``` ## 🚀 릴리스 워크플로 릴리스는 Changesets와 GitHub Actions에 의해 구동됩니다. `main` 브랜치에는 커밋된 `dist` 파일이 없으며, 소스, README, 워크플로 구성만 포함됩니다. 패키지에 영향을 주는 변경 사항에는 changeset이 포함되어야 합니다: ```sh pnpm changeset ``` 변경 사항이 `main`에 병합되면 `changesets` 워크플로가 릴리스 PR을 생성하거나 업데이트합니다. 해당 PR에는 다음 명령으로 생성된 버전 범프와 변경 로그 업데이트가 포함됩니다: ```sh pnpm changeset version ``` npm 게시 워크플로는 GitHub Actions OIDC를 통한 npm Trusted Publishing을 사용합니다. npm 패키지는 npm 패키지 게시 설정에서 이 저장소와 워크플로에 연결되어야 합니다: - 저장소: `querry-kit/nest` - 워크플로 파일: `release.yml` - 환경: 설정되지 않음 릴리스 PR이 병합된 후 `npm publish` 워크플로가 버전/변경 로그 변경 사항에 대해 실행됩니다. 이 워크플로는 패키지 검사를 실행하고, CI에서 `dist`를 빌드하고, `@querry-kit/nest`를 npm에 게시하고, 릴리스 커밋에 `vX.Y.Z` 태그를 지정하고, GitHub Release를 생성합니다. 소비자는 npm에서 설치해야 합니다: ```sh pnpm add @querry-kit/nest ``` ## 🧩 사용법 `ResourceQuery`는 일반적인 컨트롤러 흐름을 처리합니다: 선택적 `fields` 파싱, 엔드포인트 필수 include와 클라이언트 `include` 값 병합, 선택된 관계에 대한 Prisma include 생성, `QueryService` 호출, 모델을 DTO로 매핑, 응답 프로젝션. ```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), }); } ``` `QueryService`와 Prisma 호환 델리게이트로 리소스 서비스를 생성합니다: ```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); } } ``` 더 작은 가져오기를 위한 집중된 하위 경로를 사용할 수 있습니다: ```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은 선택 사항입니다. 엔드포인트가 `QueryService`를 통해 권한 인식 `where` 절을 병합해야 하는 경우에만 `ability`를 전달하고, CASL을 사용하지 않는 API에서는 생략합니다. ```ts import { createCaslAccessibleWhere } from '@querry-kit/nest/casl'; super(prisma.customer, { subject: 'Customer', accessibleWhere: createCaslAccessibleWhere({ action: 'read' }), }); ``` `query`에 ability가 전달되면 서비스는 CASL where 절을 사용자 필터와 `{ AND: [accessibleWhere, parsedWhere] }`로 결합합니다. 필드 수준 응답 권한의 경우 완성된 DTO를 변경하지 않고 필터링합니다: ```ts return filterCaslFields(dto, 'Customer', ability); ``` 이 헬퍼는 기본적으로 `read` 액션을 사용합니다. 애플리케이션이 대문자 또는 enum 기반 액션을 사용하는 경우 `{ action: RoleAction.READ }`를 전달합니다. 이는 직렬화된 DTO 필드만 필터링하며, 데이터베이스 읽기를 제한하려면 `QueryService`에 ability를 계속 전달해야 합니다. ## 🎯 Fields `fields`는 기본적으로 선택 사항입니다. 생략하면 Query Kit은 전체 DTO 응답을 반환합니다. 존재하는 경우 구문과 선택된 필드를 검증하고, 필요한 관계 include를 추가하고, 반환된 DTO를 프로젝션합니다. 외부 중괄호는 선택 사항이므로 `{id,title}`은 `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} ``` 명시적 빈 값(`?fields=`) 또는 빈 외부 선택(`?fields={}`)은 `{}`를 반환합니다. 빈 중첩 선택도 유효합니다: `items{}`는 빈 항목 객체를 생성하고, `meta{page}`는 요청된 페이지 메타데이터만 유지합니다. 공백만 포함된 값은 여전히 유효하지 않습니다. 컨트롤러가 사용자 지정 서비스 오케스트레이션이 필요한 경우 `prepareFieldsQuery`를 직접 사용합니다: ```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), }; ``` ## 📖 문서 - [시작하기](https://querry-kit.github.io/querry-kit/docs/nest/guide/getting-started) - [전체 API 예제](https://querry-kit.github.io/querry-kit/docs/nest/guide/example-app) - [CRUD 컨트롤러](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) - [DTO와 페이지네이션](https://querry-kit.github.io/querry-kit/docs/nest/api/dtos-pagination) - [OpenAPI 데코레이터](https://querry-kit.github.io/querry-kit/docs/nest/api/openapi) - [CASL](https://querry-kit.github.io/querry-kit/docs/nest/api/casl) - [API 레퍼런스](https://querry-kit.github.io/querry-kit/docs/nest/api/) ## 🛠 개발 ```sh pnpm install pnpm lint pnpm check pnpm test pnpm test:coverage pnpm build ``` 워크스페이스 빌드 허용 목록은 테스트 및 문서 도구 체인에 필요한 네이티브 스크립트(예: Parcel의 파일 감시자)를 허용합니다. `pnpm test:coverage`는 모든 소스 파일을 수집하고, 커버리지 요약을 출력하고, HTML 및 LCOV 보고서를 `coverage/`에 작성합니다. GitHub Actions는 동일한 명령을 실행하고 보고서를 워크플로 아티팩트로 보관합니다.