Skip to content

Conversation

TriPSs
Copy link
Owner

@TriPSs TriPSs commented Nov 9, 2023

TODO

  • Support @FilterableField
  • Support None paging type
  • Documentation
  • Improve naming
  • Tests

Maybe create separate package with the shared code between rest and graphql packages (Like the interceptors)?

Summary by CodeRabbit

  • New Features

    • Introduced a REST package enabling code‑first CRUD endpoints with pagination, filtering, decorators, interceptors, and authorization hooks.
    • Added a Basic REST example (Todo items, tags, subtasks) with working endpoints and OpenAPI spec.
  • Tests

    • Added E2E test for Tag endpoint and database fixtures.
  • Documentation

    • Added package README and OpenAPI spec generation helper.
  • Chores

    • Enabled Dependabot for GitHub Actions and configured stale issue handling.
    • Added @nestjs/swagger and updated project/Jest/NX configurations for the new package.

Copy link

nx-cloud bot commented Nov 9, 2023

☁️ Nx Cloud Report

CI is running/has finished running commands for commit 49cd3a4. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


✅ Successfully ran 5 targets

Sent with 💌 from NxCloud.

@codecov-commenter
Copy link

codecov-commenter commented Nov 9, 2023

Codecov Report

Attention: 192 lines in your changes are missing coverage. Please review.

Comparison is base (c11da0c) 86.28% compared to head (5bd8ccb) 79.06%.
Report is 1 commits behind head on master.

Files Patch % Lines
...ages/query-rest/src/providers/resolver.provider.ts 48.71% 14 Missing and 6 partials ⚠️
...kages/query-rest/src/decorators/field.decorator.ts 55.55% 5 Missing and 11 partials ⚠️
...s/query-rest/src/decorators/hook-args.decorator.ts 51.72% 12 Missing and 2 partials ⚠️
...-rest/src/decorators/filterable-field.decorator.ts 56.00% 7 Missing and 4 partials ⚠️
...est/src/decorators/controller-methods.decorator.ts 75.60% 4 Missing and 6 partials ⚠️
...es/query-rest/src/interceptors/hook.interceptor.ts 41.17% 9 Missing and 1 partial ⚠️
...ges/query-rest/src/auth/default-crud.authorizer.ts 63.15% 4 Missing and 3 partials ⚠️
...es/query-rest/src/connection/offset/pager/pager.ts 75.00% 3 Missing and 4 partials ⚠️
...kages/query-rest/src/decorators/decorator.utils.ts 12.50% 5 Missing and 2 partials ⚠️
...ckages/query-rest/src/resolvers/update.resolver.ts 76.66% 5 Missing and 2 partials ⚠️
... and 31 more
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #200      +/-   ##
==========================================
- Coverage   86.28%   79.06%   -7.22%     
==========================================
  Files         688      767      +79     
  Lines        9761    10623     +862     
  Branches      862      955      +93     
==========================================
- Hits         8422     8399      -23     
- Misses        619     1401     +782     
- Partials      720      823     +103     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link

nx-cloud bot commented Feb 8, 2024

☁️ Nx Cloud Report

CI is running/has finished running commands for commit e2353ec. As they complete they will appear below. Click to see the status, the terminal output, and the build insights.

📂 See all runs for this CI Pipeline Execution


🟥 Failed Commands
nx run-many --target=e2e --all
nx run-many --target=lint --all
nx run-many --target=build --all
✅ Successfully ran 2 targets

Sent with 💌 from NxCloud.

@TriPSs TriPSs marked this pull request as draft July 26, 2024 12:45
TriPSs added 11 commits August 21, 2024 17:00
…to use it

- Added `FindOneArgsType` for standardizing ID parameter handling.
- Updated delete, update, and read resolvers to use `FindOneArgsType`.
- Introduced new `id-field.decorator` for marking ID fields.
- Removed redundant `BadRequestException` in read resolver.
- Correct `FindOneArgsType` parameter in `read.resolver.ts`
- Enhance `field.decorator.ts` to properly retrieve metadata
- Add `IsNumber` and `IsString` validators for specific types
- Improve handling of array options and validation
@kasir-barati
Copy link
Contributor

@TriPSs What's going on in this PR? Kinda curious as to on what you're working in this branch. The name kinda does not ring a bell. I mean what do you mean by "rest"?

@TriPSs
Copy link
Owner Author

TriPSs commented Jan 26, 2025

@TriPSs What's going on in this PR? Kinda curious as to on what you're working in this branch. The name kinda does not ring a bell. I mean what do you mean by "rest"?

The current packages are focused on building a GraphQL API, this will add a package to create/support REST (Get, Post, Put, Delete) API (More traditional), I'm currently still building this out in inside a other project who will almost go live, so once that is done i'm back porting it here and will implement some tests so it can be released.

@kasir-barati
Copy link
Contributor

OK, so basically it will be a new package next to the others we have already. But I am worried if this will introduce extra functionality which just might complicate the source code unnecessarily. Will skim through in the next week.

@TriPSs
Copy link
Owner Author

TriPSs commented Jan 26, 2025

Well it's something I wanted/needed so i'm still going to add it, it's open for others to then also use if they don't want GraphQL.

Also since it's completely islolated in it's own package (just like GraphQL is) and uses the other services (just like the GraphQL one does) I do not see how this complicates the code base, as people only tend to look at the things they need/use (if they look at all).

@kasir-barati
Copy link
Contributor

@TriPSs OK if this has been isolated from other packages I am quite eager to see how it's been implemented. Kudos @TriPSs 💮.

@TriPSs
Copy link
Owner Author

TriPSs commented Jan 26, 2025

Well the idea is to be exactly like the GraphQL one (but with minor differences as not everything in GraphQL is available in REST), with full swagger/open api support so frontend types/libs can still be automatically generated :)

Once the internal version is live I will update this PR with the latest version, improve some naming, cleanup, add tests and merge it in for others to use.

@TriPSs
Copy link
Owner Author

TriPSs commented Sep 22, 2025

@coderabbitai full review

Copy link

coderabbitai bot commented Sep 22, 2025

✅ Actions performed

Full review triggered.

Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

Adds a new query-rest package with REST CRUD infrastructure (decorators, resolvers, interceptors, providers, paging/connection types), config and tests. Introduces a basic NestJS REST example app (DTOs, entities, modules), e2e test and OpenAPI generator/spec. Updates root configs (Jest mapping, dependency), and GitHub automation (Dependabot, stale bot).

Changes

Cohort / File(s) Summary
GitHub automation
.github/dependabot.yml, .github/stale.yml
Add Dependabot for GitHub Actions (weekly) and stale bot configuration (label-gated, 7/3 days).
Root configs
jest.preset.js, package.json
Map @ptc-org/nestjs-query-rest to source in Jest; add @nestjs/swagger dependency.
Examples: basic-rest app
examples/basic-rest/src/app.module.ts, examples/basic-rest/src/todo-item/*, examples/basic-rest/src/sub-task/*, examples/basic-rest/src/tag/*
New Nest app module; add entities, DTOs, and modules for TodoItem, SubTask, Tag using TypeORM and NestjsQueryRest.
Examples: e2e, OpenAPI, helpers
examples/basic-rest/e2e/fixtures.ts, examples/basic-rest/e2e/tag.endpoint.spec.ts, examples/helpers/generate-openapi-spec.ts, examples/basic-rest/open-api.json, examples/project.json
Seed/refresh fixtures; add Tag endpoint e2e test; helper to generate OpenAPI; committed OpenAPI spec; project config tweaks.
New package scaffolding
packages/query-rest/package.json, packages/query-rest/project.json, packages/query-rest/jest.config.ts, packages/query-rest/.babelrc, packages/query-rest/.eslintrc.json, packages/query-rest/tsconfig*.json, packages/query-rest/README.md
Introduce package metadata, Nx project, Jest, Babel/ESLint, TS configs, and README.
query-rest: auth
packages/query-rest/src/auth/*
Define authorization contracts, default authorizer factory, and token helpers; barrel export.
query-rest: common utils
packages/query-rest/src/common/*
Add DTO naming, resolver option merge, and object cleanup utilities; index.
query-rest: connection & paging
packages/query-rest/src/connection/*, packages/query-rest/src/connection/offset/**
Define connection interfaces (array/offset), page-info/connection classes, and an offset pager with results/types.
query-rest: decorators (core)
packages/query-rest/src/decorators/*
Add Field/FilterableField/IDField, controller method decorators (Get/Post/Put/Delete), authorizer decorators, hook decorators and args, query options, resolver-method utilities, SkipIf, and constants; index.
query-rest: hooks
packages/query-rest/src/hooks/*
Add hook interfaces, tokens, default hook factory, enum types, and barrel.
query-rest: interceptors
packages/query-rest/src/interceptors/*
Add Authorizer interceptor (injects authorizer) and Hook interceptor (attaches hooks); index.
query-rest: module entry
packages/query-rest/src/module.ts, packages/query-rest/src/index.ts
Add NestjsQueryRestModule.forFeature to assemble providers/endpoints; public index exports.
query-rest: providers
packages/query-rest/src/providers/*
Create providers for authorizers and hooks; resolver provider to generate endpoints/controllers.
query-rest: resolvers
packages/query-rest/src/resolvers/*
Add Create/Read/Update/Delete resolver mixins, CRUDResolver composer, base resolver interfaces, and index.
query-rest: types
packages/query-rest/src/types/**
Add mutation/query arg wrappers, rest query type, find-one args, paging (offset/none, strategies), query-args (opts, factories, defaults), validators; barrel exports.
query-rest: interfaces
packages/query-rest/src/interfaces/return-type-func.ts
Add return type function types.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant Ctl as REST Controller (Auto-generated)
  participant IntA as AuthorizerInterceptor
  participant IntH as HookInterceptor
  participant S as QueryService
  participant DB as DB/Repo

  Note over Ctl: GET /todo-item-dtos?limit&offset
  C->>Ctl: HTTP GET
  activate Ctl
  Ctl->>IntA: apply
  IntA->>Ctl: attach request.authorizer
  Ctl->>IntH: apply
  IntH->>Ctl: attach request.hooks
  Ctl->>S: queryMany(query, authorizeFilter)
  S->>DB: find with paging/filter
  DB-->>S: rows, count?
  S-->>Ctl: nodes (+pageInfo, totalCount?)
  Ctl-->>C: 200 JSON (ConnectionType)
  deactivate Ctl
Loading
sequenceDiagram
  autonumber
  participant C as Client
  participant Ctl as REST Controller (Auto-generated)
  participant IntA as AuthorizerInterceptor
  participant IntH as HookInterceptor(BEFORE_CREATE_ONE)
  participant S as QueryService
  participant DB as DB/Repo

  Note over Ctl: POST /todo-item-dtos
  C->>Ctl: HTTP POST (CreateDTO)
  activate Ctl
  Ctl->>IntA: apply
  IntA->>Ctl: attach authorizer
  Ctl->>IntH: apply
  IntH->>Ctl: attach hooks
  Ctl->>S: createOne(input)
  S->>DB: insert
  DB-->>S: created row
  S-->>Ctl: DTO
  Ctl-->>C: 201 JSON (DTO)
  deactivate Ctl
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

A rabbit taps keys in a RESTful spree,
Spinning endpoints from DTOs with glee.
Hooks hop in, authorizers stand guard,
Pages flip by offset yard by yard.
In gardens of tests, tags bloom bright—
CRUD carrots harvested, crisp and right. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "feat(rest): Created rest package" is concise, follows conventional commit style, and accurately summarizes the primary change in this PR — the addition of a new REST package (packages/query-rest) with controllers, decorators, types, and supporting files. It is specific enough for teammates scanning history to understand the main intent.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/rest

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 33

Comment on lines +10 to +17
{
"name": "id",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Path id type mismatches the DTOs.

DTO ids are numbers, but path params are strings. Switch to number (or integer) for consistency and validation.

-          "schema": { "type": "string" }
+          "schema": { "type": "number" }

Apply to sub-task, todo-item, and tag item paths.

Also applies to: 171-177, 331-337

🤖 Prompt for AI Agents
In examples/basic-rest/open-api.json around lines 10 to 17 (and similarly at
171-177 and 331-337), the path parameter "id" is declared as a string while the
DTOs use numeric ids; update the "schema" for these path parameters to use type
"integer" (or "number" if decimals are expected) and add "format": "int64" (or
"int32" as appropriate) to match the DTOs, ensuring required remains true and
validation will accept numeric ids for sub-task, todo-item, and tag endpoints.

Comment on lines +393 to +401
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTagDTO"
}
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Tag update uses create schema.

PUT updates should not accept CreateTagDTO. Add a TagUpdateDTO (optional name) and reference it here.

         "requestBody": {
           "required": true,
           "content": {
             "application/json": {
               "schema": {
-                "$ref": "#/components/schemas/CreateTagDTO"
+                "$ref": "#/components/schemas/TagUpdateDTO"
               }
             }
           }
         },

Add schema:

+"TagUpdateDTO": {
+  "type": "object",
+  "properties": {
+    "name": { "type": "string", "nullable": true }
+  }
+},
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTagDTO"
}
}
}
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/TagUpdateDTO"
}
}
}
},
Suggested change
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/CreateTagDTO"
}
}
}
"TagUpdateDTO": {
"type": "object",
"properties": {
"name": { "type": "string", "nullable": true }
}
},
🤖 Prompt for AI Agents
In examples/basic-rest/open-api.json around lines 393 to 401, the PUT
requestBody currently references CreateTagDTO which is incorrect for updates;
add a new components/schemas/TagUpdateDTO with the same properties as
CreateTagDTO but make fields optional (e.g., name optional) and replace the
requestBody schema $ref to "#/components/schemas/TagUpdateDTO"; ensure the new
schema is added under components.schemas and referenced by the PUT operation.

Comment on lines +22 to +23
@FilterableField()
todoItemId!: string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

todoItemId type should match TodoItemEntity.id (number).
Keeps the REST surface consistent with the entity and other DTOs.

   @FilterableField()
-  todoItemId!: string
+  todoItemId!: number
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@FilterableField()
todoItemId!: string
@FilterableField()
todoItemId!: number
🤖 Prompt for AI Agents
In examples/basic-rest/src/sub-task/dto/sub-task.dto.ts around lines 22 to 23,
the todoItemId property is declared as a string but should be a number to match
TodoItemEntity.id and other DTOs; change the property type from string to number
(and adjust any decorators or validators if they assume string) so the DTO's
todoItemId uses number throughout the REST surface to remain consistent with the
entity.

Comment on lines +20 to +22
@Field()
@IsNotEmpty()
todoItemId!: string
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

todoItemId should be numeric and validated.
Entity TodoItemEntity.id is a number; keep DTOs consistent and coerce/validate.

   @Field()
   @IsNotEmpty()
-  todoItemId!: string
+  @Type(() => Number)
+  @IsNumber()
+  todoItemId!: number
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Field()
@IsNotEmpty()
todoItemId!: string
@Field()
@IsNotEmpty()
@Type(() => Number)
@IsNumber()
todoItemId!: number
🤖 Prompt for AI Agents
In examples/basic-rest/src/sub-task/dto/subtask-input.dto.ts around lines 20 to
22, the todoItemId is declared as a string but the entity TodoItemEntity.id is
numeric; change the DTO to accept a numeric id and validate/transform it by
making the property a number, adding class-validator checks (e.g., @IsInt or
@IsNumber) and using class-transformer's @Type(() => Number) to coerce incoming
values to number (ensure the validation pipe is enabled so non-numeric input is
rejected).

Comment on lines +19 to +20
@ManyToMany((): ObjectType<TodoItemEntity> => TodoItemEntity, (td) => td.tags)
todoItems!: TodoItemEntity[]
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix the ManyToMany decorator usage.

The ObjectType<TodoItemEntity> usage is incorrect. TypeORM's @ManyToMany decorator expects a function returning the entity class directly.

-@ManyToMany((): ObjectType<TodoItemEntity> => TodoItemEntity, (td) => td.tags)
+@ManyToMany(() => TodoItemEntity, (td) => td.tags)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@ManyToMany((): ObjectType<TodoItemEntity> => TodoItemEntity, (td) => td.tags)
todoItems!: TodoItemEntity[]
@ManyToMany(() => TodoItemEntity, (td) => td.tags)
todoItems!: TodoItemEntity[]
🤖 Prompt for AI Agents
In examples/basic-rest/src/tag/tag.entity.ts around lines 19 to 20, the
@ManyToMany decorator is using an incorrect ObjectType<TodoItemEntity> wrapper;
change it to pass the entity class directly by replacing the first argument with
a function that returns the entity class (i.e. () => TodoItemEntity) and keep
the inverse side (td => td.tags); ensure TodoItemEntity is imported so the
decorator reads like @ManyToMany(() => TodoItemEntity, td => td.tags) and
nothing else needs to change.

Comment on lines +5 to +14
export type NonePagingType = Paging
export type OffsetPagingType = Paging

// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
export type PagingTypes = OffsetPagingType | NonePagingType
export type InferPagingTypeFromStrategy<PS extends PagingStrategies> = PS extends PagingStrategies.OFFSET
? OffsetPagingType
: PS extends PagingStrategies.NONE
? NonePagingType
: never
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Union of identical aliases erases type discrimination; add lightweight brands.

-import { Paging } from '@ptc-org/nestjs-query-core'
+import { Paging } from '@ptc-org/nestjs-query-core'
@@
-export type NonePagingType = Paging
-export type OffsetPagingType = Paging
-
-// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
-export type PagingTypes = OffsetPagingType | NonePagingType
+type Brand<T, B extends string> = T & { readonly __pagingBrand?: B }
+export type NonePagingType = Brand<Paging, 'none'>
+export type OffsetPagingType = Brand<Paging, 'offset'>
+export type PagingTypes = OffsetPagingType | NonePagingType
 export type InferPagingTypeFromStrategy<PS extends PagingStrategies> = PS extends PagingStrategies.OFFSET
   ? OffsetPagingType
   : PS extends PagingStrategies.NONE
   ? NonePagingType
   : never
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export type NonePagingType = Paging
export type OffsetPagingType = Paging
// eslint-disable-next-line @typescript-eslint/no-duplicate-type-constituents
export type PagingTypes = OffsetPagingType | NonePagingType
export type InferPagingTypeFromStrategy<PS extends PagingStrategies> = PS extends PagingStrategies.OFFSET
? OffsetPagingType
: PS extends PagingStrategies.NONE
? NonePagingType
: never
type Brand<T, B extends string> = T & { readonly __pagingBrand?: B }
export type NonePagingType = Brand<Paging, 'none'>
export type OffsetPagingType = Brand<Paging, 'offset'>
export type PagingTypes = OffsetPagingType | NonePagingType
export type InferPagingTypeFromStrategy<PS extends PagingStrategies> = PS extends PagingStrategies.OFFSET
? OffsetPagingType
: PS extends PagingStrategies.NONE
? NonePagingType
: never
🤖 Prompt for AI Agents
In packages/query-rest/src/types/query/paging/interfaces.ts around lines 5 to
14, the two paging aliases are identical which removes type discrimination; make
each alias a lightweight branded/nominal type so the union preserves
distinction. Replace NonePagingType and OffsetPagingType with intersections that
add a unique phantom property or symbol (e.g. Paging & { __pagingKind: 'none' }
and Paging & { __pagingKind: 'offset' }) and update PagingTypes and
InferPagingTypeFromStrategy to use those branded types so conditional types
correctly discriminate on PagingStrategies.

Comment on lines +1 to +6
export const DEFAULT_QUERY_OPTS = {
defaultResultSize: 10,
maxResultsSize: 50,
defaultSort: [],
defaultFilter: {}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Freeze defaults to prevent cross-request/global mutation

defaultSort: [] and defaultFilter: {} are mutable and exported; accidental mutation will leak across consumers/requests.

Apply this diff:

-export const DEFAULT_QUERY_OPTS = {
-  defaultResultSize: 10,
-  maxResultsSize: 50,
-  defaultSort: [],
-  defaultFilter: {}
-}
+export const DEFAULT_QUERY_OPTS = Object.freeze({
+  defaultResultSize: 10,
+  maxResultsSize: 50,
+  // Freeze nested structures to prevent accidental mutation
+  defaultSort: Object.freeze([] as const),
+  defaultFilter: Object.freeze({} as const)
+} as const)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export const DEFAULT_QUERY_OPTS = {
defaultResultSize: 10,
maxResultsSize: 50,
defaultSort: [],
defaultFilter: {}
}
export const DEFAULT_QUERY_OPTS = Object.freeze({
defaultResultSize: 10,
maxResultsSize: 50,
// Freeze nested structures to prevent accidental mutation
defaultSort: Object.freeze([] as const),
defaultFilter: Object.freeze({} as const)
} as const)
🤖 Prompt for AI Agents
In packages/query-rest/src/types/query/query-args/constants.ts around lines 1 to
6, the exported DEFAULT_QUERY_OPTS contains mutable defaults (defaultSort: []
and defaultFilter: {}) that can be mutated across requests; replace these with
frozen values and freeze the container to prevent accidental mutation (e.g. use
Object.freeze([]) for defaultSort, Object.freeze({}) for defaultFilter and
Object.freeze(...) on the DEFAULT_QUERY_OPTS object so none of its properties
can be altered at runtime).

Comment on lines +39 to +50
public buildQuery(): RestQuery<DTO> {
return {
query: this.query,
paging: {
limit: this.limit || opts.maxResultsSize,
offset: this.offset
},
filter: this.filter,
sorting: this.sorting,
relations: []
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fallback for maxResultsSize needs validation

The maxResultsSize fallback in buildQuery could allow unbounded queries if both limit and maxResultsSize are undefined.

  public buildQuery(): RestQuery<DTO> {
+    const effectiveLimit = this.limit || opts.maxResultsSize
+    if (effectiveLimit === undefined || effectiveLimit <= 0) {
+      throw new Error('A valid limit must be specified for queries')
+    }
    return {
      query: this.query,
      paging: {
-        limit: this.limit || opts.maxResultsSize,
+        limit: effectiveLimit,
        offset: this.offset
      },
      filter: this.filter,
      sorting: this.sorting,
      relations: []
    }
  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
public buildQuery(): RestQuery<DTO> {
return {
query: this.query,
paging: {
limit: this.limit || opts.maxResultsSize,
offset: this.offset
},
filter: this.filter,
sorting: this.sorting,
relations: []
}
}
public buildQuery(): RestQuery<DTO> {
const effectiveLimit = this.limit || opts.maxResultsSize
if (effectiveLimit === undefined || effectiveLimit <= 0) {
throw new Error('A valid limit must be specified for queries')
}
return {
query: this.query,
paging: {
limit: effectiveLimit,
offset: this.offset
},
filter: this.filter,
sorting: this.sorting,
relations: []
}
}

Comment on lines +3 to +8
/** @internal */
export function IsUndefined(validationOptions?: ValidationOptions) {
// eslint-disable-next-line @typescript-eslint/ban-types
return (obj: Object, property: string) =>
ValidateIf((o: Record<string, unknown>) => o[property] !== undefined, validationOptions)(obj, property)
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Naming and behavior mismatch; clarify contract or rename.

This decorator doesn’t validate “is undefined”; it gates other validators to run when value is not undefined. That’s easy to misuse. Options:

  • Keep behavior but rename (e.g., ValidateIfDefined), or
  • Implement a true validator that fails when value is defined.

Given this is a new public surface, align name/behavior before release to avoid breaking changes later.

Comment on lines +3 to +10
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": [
"node"
]
},
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add composite: true to enable project references build.

With packages/query-rest/tsconfig.json referencing this project, TypeScript requires the referenced project to be composite. Without it, tsc -b will fail.

Apply this diff:

   "compilerOptions": {
     "module": "commonjs",
     "outDir": "../../dist/out-tsc",
     "declaration": true,
+    "composite": true,
     "types": [
       "node"
     ]
   },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"types": [
"node"
]
},
"compilerOptions": {
"module": "commonjs",
"outDir": "../../dist/out-tsc",
"declaration": true,
"composite": true,
"types": [
"node"
]
},
🤖 Prompt for AI Agents
In packages/query-rest/tsconfig.lib.json around lines 3 to 10, the
compilerOptions block is missing "composite": true which is required for project
references; update the file by adding "composite": true to the compilerOptions
object so this project can be referenced and built with tsc -b (ensure the JSON
remains valid and commas are adjusted as needed).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants