{"id":15091390,"url":"https://github.com/simioni/nest-standard-response","last_synced_at":"2025-04-12T06:31:56.469Z","repository":{"id":200133531,"uuid":"704989254","full_name":"simioni/nest-standard-response","owner":"simioni","description":"Standardized and configurable API responses for NestJS","archived":false,"fork":false,"pushed_at":"2024-04-02T00:06:22.000Z","size":537,"stargazers_count":8,"open_issues_count":3,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-10-31T10:51:26.302Z","etag":null,"topics":["filtering","nestjs","node-js","nodejs","openapi","openapi-generator","openapi-specification","pagination","rest","rest-api","restful-api","sorting","swagger","typescript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/nest-standard-response","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simioni.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2023-10-14T17:58:20.000Z","updated_at":"2024-10-04T12:05:07.000Z","dependencies_parsed_at":"2023-10-15T22:04:42.579Z","dependency_job_id":null,"html_url":"https://github.com/simioni/nest-standard-response","commit_stats":null,"previous_names":["simioni/nest-standard-response"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-standard-response","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-standard-response/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-standard-response/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-standard-response/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simioni","download_url":"https://codeload.github.com/simioni/nest-standard-response/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223502361,"owners_count":17155938,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["filtering","nestjs","node-js","nodejs","openapi","openapi-generator","openapi-specification","pagination","rest","rest-api","restful-api","sorting","swagger","typescript"],"created_at":"2024-09-25T10:40:46.984Z","updated_at":"2024-11-07T11:04:57.878Z","avatar_url":"https://github.com/simioni.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Standardized API responses for NestJS\n\n\u003ca href=\"https://www.npmjs.com/package/nest-standard-response\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/nest-standard-response\" alt=\"NPM Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/simioni/nest-standard-response/blob/main/LICENSE\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/nest-standard-response\" alt=\"Package License\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://app.circleci.com/pipelines/github/simioni/nest-standard-response\" target=\"_blank\"\u003e\u003cimg alt=\"CircleCI\" src=\"https://img.shields.io/circleci/build/github/simioni/nest-standard-response/main?logo=circleci\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-standard-response\" target=\"_blank\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/dt/nest-standard-response?logo=npm\u0026label=installs\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-standard-response\" target=\"_blank\"\u003e\u003cimg alt=\"npm peer dependency version (scoped)\" src=\"https://img.shields.io/npm/dependency-version/nest-standard-response/peer/%40nestjs%2Fcore?logo=nestjs\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-standard-response\" target=\"_blank\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/github/languages/top/simioni/nest-standard-response?logo=typescript\"\u003e\u003c/a\u003e\n\n- Metadata-based wrapper to provide customizable and standardized API response objects;\n- Built-in handling of pagination, sorting and filtering;\n- Allows route handlers to keep returning classes instead of wrapper objects, so they remain fully compatible with interceptors;\n- Automatic OpenAPI documentation with proper response schema for all features\n- Generation of OpenAPI response examples with proper serialization for each user role\n\n\u003cbr /\u003e\n\n# Getting started\n\n## 🚀 \u0026nbsp; Install\n\n```shell\n$ npm install nest-standard-response\n```\n\u003c/br\u003e\n\n## 🔮 \u0026nbsp; Add to your app's ```imports``` array\n\n```app.module.ts```\n\n```ts\nimport { StandardResponseModule } from 'nest-standard-response';\n\n@Module({\n  imports: [\n    StandardResponseModule.forRoot(options), // options can be ommited\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\n\u003cblockquote\u003e\n\nCheck out the options that this module accepts in the [Advanced Configuration](#StandardResponseConfiguration) section.\n\n\u003c/blockquote\u003e\n\n\u003c/br\u003e\n\n## 📦 \u0026nbsp; All routes are now wrapped\n\nBy default, all routes are automatically wrapped in a standard response object:\n\n\u003ctable style=\"width: 100%\"\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// route returns dtos\n@get(\"/books\")\nlistBooks(): BookDto[] {\n  const books = [\n    new BookDto({ title: \"Dune\", year: 1965 }),\n    new BookDto({ title: \"Jaws\", year: 1974 }),\n    new BookDto({ title: \"Emma\", year: 1815 }),\n  ];\n  return books;\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// but response is wrapped\n{\n  success: true,\n  isArray: true, // auto infered\n  data: [\n    { title: \"Dune\", year: 1965 },\n    { title: \"Jaws\", year: 1974 },\n    { title: \"Emma\", year: 1815 },\n  ]\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003e To skip wrapping a particular route, just decorate the handler with [@RawResponse()](#RawResponseDecorator).\n\n\u003e It's possible to **invert** this behavior to **not wrap** any route automatically, and only wrap routes annotated with [@StandardResponse()](#StandardResponseDecorator) instead. [Check out how](#StandardResponseConfiguration-interceptAll).\n\n\u003cbr /\u003e\n\n\n## 🚦 \u0026nbsp; Wrapping only happens at the end of the NestJS' request pipeline\n\nSo interceptors like ```ClassSerializer``` and ```RoleSerializer``` work transparently without any custom logic.\n\n\u003c/br\u003e\n\n## 🔥 \u0026nbsp; Add features to your route\n\nJust decorate a route with [@StandardResponse({...options})](#StandardResponseDecorator) and pass in the options you want. Adding features will:\n\n- Automatically prepare a route to receive query parameters for that feature;\n- Parse and validate the input of these query parameters, and make them injectable into the handler;\n- Add fields to the response object to let the client know the state of these features (and to allow discoverability of defaults when the route is called without any query params);\n- Add documentation to Swagger with fully qualified schemas and examples;\n\nTo access this information during the request, use the [@StandardParam()](#StandardParamDecorator) parameter decorator to inject a params object into your handler. This object contains the parsed query params, all the configuration values you set in the ```@StandardResponse()```, plus methods to manipulate and add data into the response.\n\n\u003ctable style=\"width: 100%\"\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// route\n@get(\"/books\")\n@StandardResponse({ isPaginated: true })\nasync listBooks(\n  @StandardParam() params: StandardParams\n): BookDto[] {\n  const {\n    books,\n    count\n  } = await this.bookService.list({\n    // already validated values safe to use\n    limit: params.pagination.limit,\n    offset: params.pagination.offset,\n  });\n  // add extra information into the response\n  params.setPaginationInfo({ count: count })\n  return books;\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// response\n{\n  success: true,\n  isArray: true,\n  isPaginated: true,\n  pagination: {\n    limit: 10,\n    offset: 0,\n    defaultLimit: 10,\n    // 👇 added in handler\n    count: 33\n  },\n  data: [\n    { title: \"Dune\", year: 1965 },\n    { title: \"Jaws\", year: 1974 },\n    { title: \"Emma\", year: 1815 },\n  ]\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr /\u003e\n\n## 🎁 \u0026nbsp; Combine features!\n\nFeatures can be freely combined, or used all at once.\n\nFor example, using the features shown bellow, the route could be called like this:\n\n\u003e```/books?limit=8\u0026offset=16\u0026sort=-author,title\u0026filter=author^=Frank;year\u003e=1960;year\u003e=1970```  \n\u003e Note: This url was NOT url-encoded for readability (but you would need to encode yours)\n\n\u003ctable style=\"width: 100%\"\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// route\n@get(\"/books\")\n@StandardResponse({\n  // 👇 declare type to get OpenApi docs\n  type: [BookDto],\n  isPaginated: true,\n  defaultLimit: 12,\n  maxLimit: 20,\n  isSorted: true,\n  sortableFields: [\"title\", \"author\"],\n  isFiltered: true,\n  filterableFields: [\"author\", \"year\"],\n})\nasync listBooks(\n  @StandardParam() params: StandardParams\n): BookDto[] {\n  const {\n    books,\n    count\n  } = await this.bookService.list({\n    limit: params.pagination.limit,\n    offset: params.pagination.offset,\n    sort: params.sorting.sort,\n    filter: params.filtering.filter,\n  });\n  // 👆 to see how the 'sort' and 'filter'\n  // params are parsed, look at the \n  // SortingInfo and FilteringInfo classes\n  // in the @StandardParam() section of\n  // this document\n\n  // 👇 add extra information into the response\n  params.setPaginationInfo({ count: count })\n  params.setMessage('A full-featured example!')\n  return books;\n}\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// response\n{\n  success: true,\n  message: \"A full-featured example!\",\n  isArray: true,\n  isPaginated: true,\n  isSorted: true,\n  isFiltered: true,\n  pagination: {\n    query: \"limit=8\u0026offset=16\",\n    limit: 8,\n    offset: 16,\n    defaultLimit: 12,\n    maxLimit: 20,\n    count: 33\n  },\n  sorting: {\n    sortableFields: [\"title\", \"author\"],\n    query: \"-author,title\",\n    sort: [\n      {\n        field: \"author\",\n        order: \"des\"\n      },\n      {\n        field: \"title\",\n        order: \"asc\"\n      }\n    ]\n  },\n  filtering: {\n    filterableFields: [\"author\", \"year\"],\n    query: \"author^=Frank;year\u003e=1960;year\u003e=1970\",\n    filter: {\n      allOf: [\n        { anyOf: [\n          {\n            field: 'author',\n            operation: '^=',\n            value: \"Frank\"\n          },\n        ]},\n        { anyOf: [\n          {\n            field: 'year',\n            operation: '\u003e=',\n            value: 1960\n          },\n        ]},\n        { anyOf: [\n          {\n            field: 'year',\n            operation: '\u003c=',\n            value: 1970\n          },\n        ]}\n      ]\n    }\n  },\n  data: [ ... ]\n}\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\nFor detailed information on the objects generated by filtering and sorting, as well as a list of all operations available, see the documentation for the [@StandardParam()](#StandardParamDecorator) decorator.\n\n\n\u003cbr /\u003e\n\n---------------------------------------------------------------------------\n\n\u003cbr /\u003e\n\n# Reference\n* [@StandardResponse()](#StandardResponseDecorator) \u003csup\u003edecorator\u003c/sup\u003e\n  * [StandardResponseOptions](#StandardResponseOptions)\n* [@RawResponse()](#RawResponseDecorator) \u003csup\u003edecorator\u003c/sup\u003e\n* [@StandardParam()](#StandardParamDecorator) \u003csup\u003eparameter decorator\u003c/sup\u003e\n* [Advanced Configuration](#StandardResponseConfiguration)\n\n\u003c/br\u003e\n\u003c/br\u003e\n\n## 🟠 \u0026nbsp; @StandardResponse(_options?:_ [_StandardResponseOptions_](#StandardResponseOptions)) \u003ca name=\"StandardResponseDecorator\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\nA decorator that wraps the return of a route into a standardized API response object (while still allowing the handler to return true DTOs or other model class instances — this makes interceptors like caching, ```ClassSerializer```, or ```RoleSerializer``` work transparently.)\n\nThe wrapper allows custom messages to be set in the response, and has optional features to handle common tasks, like **pagination, sorting and filtering**.\n\nIt can also optionally apply swagger's documentation, providing the correct combined schema for the DTO and the wrapper including any of its features. If given an array of Roles, it can also build Swagger route response examples for each user role, containing the reponse as it would be serialized for that user group.\n\n\u003cbr/\u003e\n\n``` ts\nimport { UserDto } from './dto/user.dto';\n\n@Controller('users')\nexport class UsersController {\n  constructor(\n    private readonly usersService: UsersService,\n  ) {}\n\n  @Get('/')\n  @StandardResponse({ type: [UserDto] })\n  async findAll(): Promise\u003cUserDto[]\u003e {\n    const users = await this.usersService.findAll();\n    return users // \u003c--- returns an array of UserDtos\n  }\n}\n\n// get /api/users\n// Response:\n{\n  \"success\": true,\n  \"isArray\": true,\n  \"data\": [\n    Users... // \u003c--- The returned array is delivered inside the data property\n  ]\n}\n```\n\n(TODO image of swagger UI with the response examples dropdown open. Comparing a response for User and Admin, with arrows showcasing the extra fields returned only to admin)\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## 🔸 \u0026nbsp; StandardResponseOptions \u003ca name=\"StandardResponseOptions\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eOption\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003etype\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eClass\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe class that represents the object(s) that will be returned from the route (for example, a Model or a DTO). This option is required to get auto-documentation.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003edescription\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eUsed as the desciption field of the response in the OpenAPI docs.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eisPaginated\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eboolean\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eMark the route to serve paginated responses, and allow the use of pagination options. This will capture and validate \u003ccode\u003elimit\u003c/code\u003e and \u003ccode\u003eoffset\u003c/code\u003e query parameters, and make them available in the handler via \u003ccode\u003e@StandardParam\u003c/code\u003e. Also sets up pagination fields in the response object. \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eisSorted\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eboolean\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eMark the route to serve sorted responses, and allow the use of sorting options. This will capture and validate the \u003ccode\u003esort\u003c/code\u003e query parameter, and make it available in the handler via \u003ccode\u003e@StandardParam\u003c/code\u003e. Also sets up sorting fields in the response object. \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eisFiltered\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eboolean\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eMark the route to serve filtered responses, and allow the use of filtering options. This will capture and validate the \u003ccode\u003efilter\u003c/code\u003e query parameter, parse it into a \u003ccode\u003eFilteringQuery\u003c/code\u003e, an and make it available in the handler via \u003ccode\u003e@StandardParam\u003c/code\u003e. Also sets up filtering fields in the response object. \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003edefaultLimit\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e\u003cb\u003e(Pagination option) \u003c/b\u003e\u003c/i\u003eThe value to used for \u003ccode\u003elimit\u003c/code\u003e if the query param is missing. \u003ci\u003e\u003cb\u003e(Defaults to 10)\u003c/b\u003e\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003emaxLimit\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e\u003cb\u003e(Pagination option) \u003c/b\u003e\u003c/i\u003eThe maximum value accepted by the \u003ccode\u003elimit\u003c/code\u003e query param.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eminLimit\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e\u003cb\u003e(Pagination option) \u003c/b\u003e\u003c/i\u003eThe minimum value accepted by the \u003ccode\u003elimit\u003c/code\u003e query param.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esortableFields\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e\u003cb\u003e(Sorting option) \u003c/b\u003e\u003c/i\u003eA list of fields that can used for sorting. If left undefined, all fields will be accepted. An empty array allows no fields.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003e\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efilterableFields\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e\u003cb\u003e(Filtering option) \u003c/b\u003e\u003c/i\u003eA list of fields that can used for filtering. If left undefined, all fields will be accepted. An empty array allows no fields.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr /\u003e\n\n---------------------------------------------------\n\n\u003c/br\u003e\n\n## 🟠 \u0026nbsp;  @RawResponse() \u003ca name=\"RawResponseDecorator\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\nThe default behavior of StandardResponse is to wrap the response from all routes application wide. This keeps the API consistent and predictable. However, if you need to skip this behavior for a particular route, just set the ```@RawResponse()``` decorator:\n\n```ts\n@Controller('external-api-integration')\nexport class ExternalApiIntegrationController {\n  @Get('/')\n  @RawResponse() // \u003c--- will skip wrapping\n  async findAll(): Promise\u003cSomeCustomObject\u003e {\n    return customObject;\n  }\n}\n```\n\n\nIf you're adding StandardResponse into an existing app, it might be useful to invert this behavior to create a gradual transition path. To do this, set the ```interceptAll``` option to ```false``` when importing the ```StandardResponseModule``` in your application. This way, routes will only be wrapped if they have explicitly set the ```@StandardResponse()``` decorator. See more information in the \"Configuring\" section bellow.\n\n\u003c/br\u003e\n\n---------------------------------------------------\n\n\u003c/br\u003e\n\n## 🟠 \u0026nbsp; @StandardParam() \u003ca name=\"StandardParamDecorator\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\nA parameter decorator used to inject a ```StandardParams``` object in the route handler.\n\nThis object allows access to:\n\n* All options set in ```@StandardResponse()```;\n* Information captured from query parameters, parsed and validated;\n* Methods to include and modify fields in the response object;\n\n\u003cbr /\u003e\n\n``` ts\nimport { UserDto } from './dto/user.dto';\n\n@Controller('users')\nexport class UsersController {\n  constructor(\n    private readonly usersService: UsersService,\n  ) {}\n\n  @Get('/')\n  @StandardResponse({\n    type: [UserDto],\n    isPaginated: true,\n    maxLimit: 24,\n    defaultLimit 12,\n  })\n  async findAll(\n    @StandardParam() params: StandardParams // \u003c--- inject into handler\n  ): Promise\u003cUserDto[]\u003e {\n    const [users, count] = await this.usersService.findAll({\n      limit: params.pagination.limit,\n      offset: params.pagination.offset,\n    });\n    params.setPaginationInfo({ count: 348 }) // \u003c--- set additional info\n    return users;\n  }\n}\n\n// get /api/users?limit=15\u0026offset=30\n// Response:\n{\n  \"success\": true,\n  \"isArray\": true,\n  \"isPaginated\": true,\n  \"pagination: {\n    count: 348, // \u003c--- added inside the handler\n    limit: 15, // \u003c--- from query\n    offset: 30,\n    maxLimit: 24, // \u003c--- from decorator options\n    defaultLimit: 12,\n  }\n  \"data\": [\n    Users...\n  ]\n}\n```\n\n\u003cbr /\u003e\n\nThe params object injected with @StandardParam() contains these keys:\n\n\u003ctable style=\"width: 100%;\"\u003e\n  \u003ctr\u003e\n    \u003cth\u003eKey\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003epagination\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003ePaginationInfo\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eOnly available when the response \u003ccode\u003eisPaginated\u003c/code\u003e option is \u003ccode\u003etrue\u003c/code\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esorting\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eSortingInfo\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eOnly available when the response \u003ccode\u003eisSorted\u003c/code\u003e option is \u003ccode\u003etrue\u003c/code\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efiltering\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eFilteringInfo\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eOnly available when the response \u003ccode\u003eisFiltered\u003c/code\u003e option is \u003ccode\u003etrue\u003c/code\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esetPaginationInfo()\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e(info: {}) =\u003e void\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAllows modifying the pagination metadata inside the route handler to add extra information or to reflect some dynamic condition. For example, to add a pagination \u003ccode\u003ecount\u003c/code\u003e. The object passed to this method will be merged with the current information, so partial updates are OK.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esetSortingInfo()\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e(info: {}) =\u003e void\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAllows modifying the sorting metadata inside the route handler.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esetFilteringInfo()\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e(info: {}) =\u003e void\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAllows modifying the filtering metadata inside the route handler.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esetMessage()\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e(message: string) =\u003e void\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAllows setting a custom message in the response object.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr /\u003e\n\n## 🔸 \u0026nbsp; PaginationInfo\n\n\u003ctable style=\"width: 100%;\"\u003e\n  \u003ctr\u003e\n    \u003cth\u003eProperty\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003equery?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe original string from the request for the \u003ccode\u003elimit\u003c/code\u003e and \u003ccode\u003eoffset\u003c/code\u003e query params. \u003cb\u003e[ReadOnly]\u003c/b\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003elimit?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eHow many items to send. This is the same as the \u003ccode\u003elimit\u003c/code\u003e query param, but parsed and validated.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eoffset?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eHow many items to skip. This is the same as the \u003ccode\u003eoffset\u003c/code\u003e query param, but parsed and validated.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ecount?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe total count of items that are being paginated. This value needs to be set inside the handler using the \u003ccode\u003esetPaginationInfo()\u003c/code\u003e method.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003emaxLimit?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe maximum value accepted by the \u003ccode\u003elimit\u003c/code\u003e query param. \u003cb\u003e[ReadOnly]\u003c/b\u003e \u003ci\u003e(From the options set in \u003ccode\u003e@StandardResponse()\u003c/code\u003e).\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eminLimit?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe minimum value accepted by the \u003ccode\u003elimit\u003c/code\u003e query param. \u003cb\u003e[ReadOnly]\u003c/b\u003e \u003ci\u003e(From the options set in \u003ccode\u003e@StandardResponse()\u003c/code\u003e).\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003edefaultLimit?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003enumber\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe default number of items to send if no query \u003ccode\u003elimit\u003c/code\u003e is provided. \u003cb\u003e[ReadOnly]\u003c/b\u003e \u003ci\u003e(From the options set in \u003ccode\u003e@StandardResponse()\u003c/code\u003e).\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## 🔸 \u0026nbsp; SortingInfo\n\n\u003ctable style=\"width: 100%;\"\u003e\n  \u003ctr\u003e\n    \u003cth\u003eProperty\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003equery?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe original string from the request for the \u003ccode\u003esort\u003c/code\u003e query param.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esortableFields?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eA list of all the fields that can used for sorting. \u003cb\u003e[ReadOnly]\u003c/b\u003e \u003ci\u003e(From the options set in \u003ccode\u003e@StandardResponse()\u003c/code\u003e).\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003esort?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eSortingOperation[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAn array of \u003ccode\u003eSortingOperation\u003c/code\u003e objects parsed from the query.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd colspan=\"3\"\u003e\u0026nbsp;\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003eSortingOperation\u003cth\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efield\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe name of the field being sorted.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eorder\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e'asc' | 'des'\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eOrder of the sorting operation. These strings are available in an enum for static typing: \u003ccode\u003eSortingOrder.ASC\u003c/code\u003e and \u003ccode\u003eSortingOrder.DES\u003c/code\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## 🔸 \u0026nbsp; FilteringInfo\n\n\u003ctable style=\"width: 100%;\"\u003e\n  \u003ctr\u003e\n    \u003cth\u003eProperty\u003c/th\u003e\n    \u003cth\u003eType\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003equery?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe original string from the request for the \u003ccode\u003efilter\u003c/code\u003e query param.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efilterableFields?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eA list of all the fields that can used for filtering. \u003cb\u003e[ReadOnly]\u003c/b\u003e \u003ci\u003e(From the options set in \u003ccode\u003e@StandardResponse()\u003c/code\u003e).\u003c/i\u003e\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efilter?\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003e{ allOf: FilteringQueryGroup[] }\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eFilter is an object parsed from the query containing a single property: \u003cb\u003eallOf\u003c/b\u003e. This is an array of \u003ccode\u003eFilteringQueryGroup\u003c/code\u003e objects. All of these filter groups should be combined using an \u003cb\u003eAND\u003c/b\u003e operation.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd colspan=\"3\"\u003e\u0026nbsp;\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003eFilteringQueryGroup\u003cth\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eanyOf\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eFilteringQueryOperation[]\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eAn array of \u003ccode\u003eFilteringQueryOperation\u003c/code\u003e objects. These filters should be combined using an \u003cb\u003eOR\u003c/b\u003e operation.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\u003ctd colspan=\"3\"\u003e\u0026nbsp;\u003c/td\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003cth colspan=\"3\"\u003eFilteringQueryOperation\u003cth\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003efield\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eName of the field to filter on.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eoperation\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eThe comparison operation to perform. Possible operators are bellow.\n    \u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003evalue\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003estring\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003eValue used for the comparison.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\n\u003ctable style=\"width: 100%\"\u003e\n  \u003ctr\u003e\n    \u003cth\u003eOperation\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n    \u003cth\u003eURL Encoded Form\u003c/th\u003e\n    \u003cth\u003eExample\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e==\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eEquals\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3D%3D\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e!=\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eNot Equals\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e!%3D\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003c=\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eLess than or equal to\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3C%3D\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003c\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eLess than\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3C\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003e=\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eGreater than or equal to\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3E%3D\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e\u003e\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eGreater than\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3E\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e=@\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eContains\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3D@\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e!@\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eDoes not contain\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e!@\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e=^\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eStarts with\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3D%5E\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003e=$\u003c/td\u003e\n    \u003ctd\u003e\u003ci\u003eEnds with\u003c/i\u003e\u003c/td\u003e\n    \u003ctd\u003e%3D%24\u003c/td\u003e\n    \u003ctd\u003e.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br \u003e\n\n\u003cblockquote\u003e\nThese rules are similar to other APIs like \u003ca href=\"https://developers.google.com/analytics/devguides/reporting/core/v3/reference#filters\"\u003eGoogle Analytics\u003c/a\u003e or \u003ca href=\"https://developer.matomo.org/api-reference/reporting-api-segmentation\"\u003eMatomo Analytics\u003c/a\u003e.\n\u003c/blockquote\u003e\n\n\u003c/br\u003e\n\n## 🔸 \u0026nbsp; Building the search query\n\nWhen building a query, all **AND** operations should be separated by a **semicolon (;)**, and all **OR** operations should be separed by a **comma (,)**. For example:\n\nThis query will filter all books available for lending, which were first published in France OR Italy, between 1970 AND 1999, whose author starts with Vittorio OR ends with Alatri:\n\n```\navailable==true;country==France,country==Italy;year\u003e=1970;year\u003c=1999;author=^Vittorio,author=$Alatri\n```\n\nThe resulting parsed object from this query will be:\n\n```ts\n{ allOf: [\n  { anyOf: [\n    { field: 'available', operation: '==', value: true },\n  ]},\n  { anyOf: [\n    { field: 'country', operation: '==', value: 'France' },\n    { field: 'country', operation: '==', value: 'Italy' },\n  ]},\n  { anyOf: [\n    { field: 'year', operation: '\u003e=', value: 1970 },\n  ]},\n  { anyOf: [\n    { field: 'year', operation: '\u003c=', value: 1999 },\n  ]},\n  { anyOf: [\n    { field: 'author', operation: '=^', value: 'Vittorio' },\n    { field: 'author', operation: '=$', value: 'Alatri' },\n  ]},\n]}\n```\n\u003c/br\u003e\n\n---------------------------------------------------\n\n\u003c/br\u003e\n\u003c/br\u003e\n\u003c/br\u003e\n\n# 🟠 \u0026nbsp; Advanced configuration \u003ca name=\"StandardResponseConfiguration\"\u003e\u003c/a\u003e\n\n## ✅ validateResponse\n\nAllows you to provide a validation function to stop the return of a route if certain conditions are met.\n\nFor example: this can abort a request if a route tries to return — instead a DTO — a raw DB document or some other object that may leak information not intended to be exposed.\n\nThis function should return ```false``` to abort the request.\n\n```ts\n@Module({\n  imports: [\n    StandardResponseModule.forRoot({\n      validateResponse: (data) =\u003e {\n        if (isMongooseObject(data)) return false;\n        return true;\n      },\n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\n\u003cbr/\u003e\n\n## ✅ interceptAll \u003ca name=\"StandardResponseConfiguration-interceptAll\"\u003e\u003c/a\u003e\n\nSetting ```interceptAll``` to ```false``` will invert the default behavior of wrapping all routes by default, and will instead only wrap routes decorated with ```@StandardResponse()```.\n\n```ts\n@Module({\n  imports: [\n    StandardResponseModule.forRoot({\n      interceptAll: false\n    }),\n  ],\n  controllers: [AppController],\n  providers: [AppService],\n})\nexport class AppModule {}\n```\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n---------------------------------------------------\n\n\u003cbr /\u003e\n\n## 🚀 \u0026nbsp; TODO Milestones\n\n- Allow setting any custom field in the repsonse object by exposing a method in the StandardParam: ```setExtra(field, value)```;\n\n\u003c/br\u003e\n\n\n🏭 ⭐️ 🕹️ 💡 💎 🔩 ⚙️ 🧱 🔮 💈 🛍️ 🎁 🪭 ⚜️ ❇️ 🚩\n📦 🏷️ 📮 \n🟠 🟧 🔶 🔸\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimioni%2Fnest-standard-response","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimioni%2Fnest-standard-response","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimioni%2Fnest-standard-response/lists"}