{"id":15065964,"url":"https://github.com/simioni/nest-pret","last_synced_at":"2025-04-10T13:40:37.457Z","repository":{"id":195059565,"uuid":"692140754","full_name":"simioni/nest-pret","owner":"simioni","description":"A generator to bootstrap fully-featured NestJS apps. Includes user registration, email verification, password recovery, claims-based access control, standardized responses with pagination, filtering and sorting, standardized error handling, OpenAPI auto-documentation and full e2e testing.","archived":false,"fork":false,"pushed_at":"2023-12-19T16:37:41.000Z","size":1846,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-18T18:55:26.698Z","etag":null,"topics":["api","casl","docker","docker-compose","docker-swarm","e2e","jest","mermaidjs","mongodb","mongoose","nestjs","nodejs","nodemailer","openapi","pactumjs","passportjs","rest-api","swagger","typescript"],"latest_commit_sha":null,"homepage":"","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,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-09-15T16:44:56.000Z","updated_at":"2024-06-28T20:18:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"e0cbc918-d320-41ef-916c-9f724d67689e","html_url":"https://github.com/simioni/nest-pret","commit_stats":null,"previous_names":["simioni/nest-mongoose-passport-casl","simioni/nest-pret"],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-pret","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-pret/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-pret/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simioni%2Fnest-pret/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simioni","download_url":"https://codeload.github.com/simioni/nest-pret/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248225936,"owners_count":21068087,"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":["api","casl","docker","docker-compose","docker-swarm","e2e","jest","mermaidjs","mongodb","mongoose","nestjs","nodejs","nodemailer","openapi","pactumjs","passportjs","rest-api","swagger","typescript"],"created_at":"2024-09-25T00:58:24.744Z","updated_at":"2025-04-10T13:40:37.433Z","avatar_url":"https://github.com/simioni.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# A generator to bootstrap fully-featured NestJS apps\n\n\u003ca href=\"https://www.npmjs.com/package/nest-pret\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/nest-pret\" alt=\"NPM Version\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/simioni/nest-pret/blob/main/LICENSE\" target=\"_blank\"\u003e\u003cimg src=\"https://img.shields.io/npm/l/nest-pret\" alt=\"Package License\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-pret\" target=\"_blank\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/npm/dt/nest-pret?logo=npm\u0026label=installs\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-pret\" target=\"_blank\"\u003e\u003cimg alt=\"npm peer dependency version (scoped)\" src=\"https://img.shields.io/npm/dependency-version/nest-pret/%40nestjs%2Fcore?logo=nestjs\"\u003e\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/nest-pret\" target=\"_blank\"\u003e\u003cimg alt=\"npm\" src=\"https://img.shields.io/github/languages/top/simioni/nest-pret?logo=typescript\"\u003e\u003c/a\u003e\n\u003c!-- \u003ca href=\"https://app.circleci.com/pipelines/github/simioni/nest-pret\" target=\"_blank\"\u003e\u003cimg alt=\"CircleCI\" src=\"https://img.shields.io/circleci/build/github/simioni/nest-pret/main?logo=circleci\"\u003e\u003c/a\u003e --\u003e\n\n\n### Generates code that is tested, documented, and production-ready with zero downtime continuous deployment.\n\u003c/br\u003e\n\nThe generated app solves much of the functionality required from a modern web app:\n\n- User registration\n- Password recovery\u003c!-- - User consent for TOS, Cookies, Policies, etc --\u003e\n- E-mail verification, configurable between:\n  - ***required*** before login\n  - ***delayed*** until a route with `EmailVerifiedGuard` enforces it\n  - or ***off***\n- Claims-based access control, including:\n  - Restricted access to routes via policies\n  - Restricted access to specific documents by ownership or other conditional constraints\n  - Serialization of response objects exposing only the fields the user has access to\n- Standardized API responses, including:\n  - Automatic wrapping of return objects into a StandardResponse\n  - Metadata-based — handlers remains returning Classes compatible with interceptors\n  - Handling of pagination, sorting and filtering\n  - Generation of OpenAPI documentation for routes with the proper combined response schema\n  \u003c!-- - Generation of OpenAPI response examples with proper serialization for each user role --\u003e\n- Secure defaults:\n  - Sets secure HTTP response headers\n  - Global validation of all request inputs\n  - Global validation of response values before serialization\n  - Rate-limiting across the app with tighter limits for account creation\n- Configurable\n  - Config module parses and validates .env variables during bootstrap\n  - Config service makes them available app-wide with proper type definitions\n- Deployable\n  - Docker compose environmets for dev and e2e testing\n  - Docker swarm stack ready for continuous deployment\n- Tested\n  - Complete end-to-end testing suites\n  - 100% coverage of all user interaction flows\n\n\u003cbr /\u003e\n\n# 🚀 Getting started\n\nIn a machine with npm installed, run:\n```sh\nnpx nest-pret@latest new\n```\n\nAfter the generator have bootstraped your new app:\n\n```\ncd myapp\nnpm run dev\n```\n\n\u003cbr /\u003e\n\nThis will start\n- the dev database\u003c!-- - a redis instance; --\u003e\n- the NestJS app in watch mode: `localhost:3000`\n- Mongo-express visual DB admin*: `localhost:8081`\n- Swagger UI documentation explorer*: `localhost:3000/dev-tools/docs`\n- MermaidJS App Graph*: `localhost:3000/dev-tools/graph`\n\n\u003e These features are only started when running in `development` env.\n\n\u003cbr /\u003e\n\nRemember to edit the ```.env``` file and add your mailer service information to get mailer features.\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# 🚦 Managing the app\n\n|Command|Description|\n|----|-----------|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;dev\u003c/code\u003e|Use for local development. This will start docker as `development` and keep the app in `watch mode` inside of it.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;dev:stop\u003c/code\u003e|Stops all containers created by running the `dev` command.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;test\u003c/code\u003e|Run tests locally.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;e2e\u003c/code\u003e|Starts docker *as* `production` and run the `e2e` tests inside of it.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;deploy\u003c/code\u003e|Once you're ready to publish to production, this stars the continuous deployment pipeline. See [running in production](#runningInProduction). |\n\n\u003cbr /\u003e\n\nSome of the scripts can be started in watch mode:\n\n|Command|Description|\n|----|-----------|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;test:watch\u003c/code\u003e|Run all tests locally and keep watching for changes.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;e2e:watch\u003c/code\u003e|Starts docker *as* `production` and run all e2e tests inside of it. Keep test containers alive and will re-run changed tests.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;e2e:stop\u003c/code\u003e|Stops all test containers keept alive by running `e2e:watch`.|\n\n\u003cbr /\u003e\n\nTo see how the app behaves in production, you can run the deployment `stack` on a local `docker swarm` using the commands:\n\n|Command|Description|\n|----|-----------|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;prod\u003c/code\u003e|Will start a stack as `production` in the local machine docker engine. This requires docker to have the swarm orchestrator active. If not, you will need to run \u003ccode\u003edocker\u0026nbsp;swarm\u0026nbsp;init\u003c/code\u003e first.|\n|\u003ccode\u003enpm\u0026nbsp;run\u0026nbsp;prod:stop\u003c/code\u003e|Stops all docker swarm services created by running the `prod` command.|\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# 🐳 Running in production \u003ca name=\"runningInProduction\"\u003e\u003c/a\u003e\n\n### Prepare the servers:\n1. Start one or more servers or VPSs on your cloud provider of choice and install Docker on them;\n2. Start docker in [swarm mode](https://docs.docker.com/engine/swarm/swarm-mode/); If running multiple servers, add them to the swarm;\n\n\u003cbr /\u003e\n\n### On your local machine:\n\n1. Make sure to edit the `.env` file to add the correct production information for your domain, mailer service, SSH key location, and a private container registry where the application container will be published to.\n2. Make sure your git working directory is clean. Merge all changes that you want to be included in this release or stash them.\n3. Start the deployment pipeline by running:\n\n```\nnpm run deploy\n```\n\n\u003cbr /\u003e\n\n### 🔥 Done!\n\nThe deployment pipeline will:\n\n- Run all tests and e2e tests;\n- Build the app;\n- Bump the npm version and create a tagged git commit;\n- Build the container image and push it to the registry;\n- SSH into the docker swarm manager node;\n- Update the deployed stack with the new services;\n\nOnce the new stack is applied, the swarm will start a zero downtime rolling update of changed containers one at a time.\n\n\u003cbr /\u003e\n\n\u003e Deployments will default to building a new *patch* release. You can specify another semversion, for example for a *minor* release, run:\n\u003e\n\u003e `npm run deploy -- -v minor`\n\u003e\n\u003e To see all options available to the *deploy.sh* script, run:\n\u003e `npm run deploy -- --help`.\n\n\u003cbr /\u003e\n\n### Rolling back failed updates\n\nIf the deployed containers are crashing, docker will stop rolling out any new containers and will reroute traffic to the replicas that are still running the previous image. You can rollback the updated containers by running:\n\n```\nnpm run rollback\n```\n\n\u003cbr /\u003e\n\n---------------------------------------------------------------------------\n# Reference \u003ca name=\"RefIndex\"\u003e\u003c/a\u003e\n\n* [Tech stack](#TechStack)\n* [App Graph](#AppGraph)\n* [Models as a Single Source of Truth (SSOT)](#AboutModels)\n\nThe Code:\n\n* [Auth Module](#AuthModule) 🚪\n* [Policies Module](#PoliciesModule) 🏛️\n  * [CaslAbilityFactory](#CaslAbilityFactory)\n  * [PoliciesGuard](#PoliciesGuard) \u003csup\u003eguard\u003c/sup\u003e\n  * [@CheckPolicies()](#CheckPoliciesDecorator) \u003csup\u003edecorator\u003c/sup\u003e\n  * [@UserAbilityParam()](#UserAbilityParamDecorator) \u003csup\u003eparameter decorator\u003c/sup\u003e\n* [User Module](#UserModule) 👤\n  * [EmailVerifiedGuard](#EmailVerifiedGuard) \u003csup\u003eguard\u003c/sup\u003e\n  * [EmailOrIdPipe](#EmailOrIdPipe) \u003csup\u003epipe\u003c/sup\u003e\n* [Mailer Module](#MailerModule) 📮\n* [Config Module](#ConfigModule) ⚙️\n* [StandardResponse Module](#StandardResponseModule) 📦\n  * [@StandardResponse()](#StandardResponseDecorator) \u003csup\u003edecorator\u003c/sup\u003e\n    * [StandardResponseOptions](#StandardResponseOptions)\n    * [@StandardParam()](#StandardParamDecorator) \u003csup\u003eparameter decorator\u003c/sup\u003e\n  * [@RawResponse()](#RawResponseDecorator) \u003csup\u003edecorator\u003c/sup\u003e\n  * [Advanced Configuration](#StandardResponseConfiguration)\n* [Testing Factories](#TestModule) 🧪\n  * [TestingServerFactory](#TestingServerFactory)\n  * [UserStubFactory](#UserStubFactory)\n\n\u003c/br\u003e\n\n# Tech stack \u003ca name=\"TechStack\"\u003e\u003c/a\u003e\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n* Docker [compose](https://github.com/docker/compose) for development and testing, and [swarm](https://dockerswarm.rocks/) for deployment;\n* Typescript\n* MongoDB\n* NestJS \u003csup\u003e[source](https://github.com/nestjs/nest)\u003c/sup\u003e\n* Mongoose \u003csup\u003e[source](https://github.com/Automattic/mongoose)\u003c/sup\u003e\n* PassportJS \u003csup\u003e[source](https://github.com/jaredhanson/passport)\u003c/sup\u003e\n* Casl \u003csup\u003e[source](https://github.com/stalniy/casl)\u003c/sup\u003e\n* Nodemailer \u003csup\u003e[source](https://github.com/nodemailer/nodemailer)\u003c/sup\u003e\n* Nest Standard Response \u003csup\u003e[source](https://github.com/simioni/nest-standard-response)\u003c/sup\u003e\n* Jest \u003csup\u003e[source](https://github.com/jestjs/jest)\u003c/sup\u003e\n* PactumJS \u003csup\u003e[source](https://github.com/pactumjs/pactum)\u003c/sup\u003e\n* NestJS Spelunker \u003csup\u003e[source](https://github.com/jmcdo29/nestjs-spelunker)\u003c/sup\u003e\n* MermaidJS \u003csup\u003e[source](https://github.com/mermaid-js/mermaid)\u003c/sup\u003e\n* Swagger / OpenAPI \u003csup\u003e[source](https://github.com/swagger-api)\u003c/sup\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# App Graph \u003ca name=\"AppGraph\"\u003e\u003c/a\u003e\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\nThese are the modules included in the generated app and how they interact with each other.\n\n```mermaid\n%%{ init: { 'flowchart': { 'curve': 'monotoneX' }, 'theme':'dark' } }%%\nflowchart LR\n\tsubgraph legend[ Legend ]\n\t\tdirection LR\n\t\tsubgraph legendLine1 [ ]\n\t\t\tdirection TB\n\t\t\tex1(Module)\n\t\t\tex2([Global Module]):::globalModule\n\t\t\tex3{{fa:fa-globe Controller}}:::controller\n\t\t\tex9([fa:fa-bell-concierge Service]):::service\n\t\t\tex4([fa:fa-briefcase Provider]):::provider\n\t\tend\n\t\tsubgraph legendLine2 [ ]\n\t\t\tdirection TB\n\t\t\tex6{{fa:fa-fish-fins Global Pipe}}:::pipe\n\t\t\tex7{{fa:fa-bullseye Global Interceptor}}:::interceptor\n\t\t\tex8{{fa:fa-shield-halved Global Guard}}:::guard\n\t\t\tex5([fa:fa-database Model]):::model\n\t\tend\n\tend\n\tsubgraph globalModules[ ]\n\t\tConfigModule([ConfigModule]):::globalModule\n\t\tJwtModule([JwtModule]):::globalModule\n\t\tConfigHostModule([ConfigHostModule]):::globalModule\n\t\tMongooseCoreModule([MongooseCoreModule]):::globalModule\n\tend\n\tsubgraph modules[\" \"]\n\t\tdirection LR\n\t\tsubgraph AppModule\n\t\t\tdirection LR\n\t\t\tPipe{{fa:fa-fish-fins ValidationPipe}}:::pipe\n\t\t\tSerializer{{fa:fa-fish-fins RolesSerializerInterceptor}}:::interceptor\n\t\t\tAppService([fa:fa-bell-concierge AppService]):::service\n\t\tend\n\t\tsubgraph ConfigModule[ ]\n      subgraph ConfigModulePadding[ConfigModule]\n      end\n\t\tend\n\t\tsubgraph ConfigHostModule[ ]\n      subgraph ConfigHostModulePadding[ConfigHostModule]\n      end\n\t\tend\n\t\tsubgraph MongooseModule\n\t\t\tdirection LR\n\t\t\tUserModel([fa:fa-database UserModel]):::model\n\t\t\tEmailVerificationModel([fa:fa-database EmailVerificationModel]):::model\n\t\t\tForgottenPasswordModel([fa:fa-database ForgottenPasswordModel]):::model\n\t\tend\n\t\tsubgraph MongooseCoreModule[ ]\n      subgraph MongooseCoreModulePadding[MongooseCoreModule]\n      end\n\t\tend\n\t\tsubgraph StandardResponseModule\n\t\t\tdirection LR\n\t\t\tInterceptor{{fa:fa-bullseye StandardResponseInterceptor}}:::interceptor\n\t\tend\n\t\tsubgraph AuthModule\n\t\t\tdirection LR\n\t\t\tAuthController{{fa:fa-globe AuthController}}:::controller\n\t\t\tAuthService([fa:fa-bell-concierge AuthService]):::service\n\t\t\tJwtStrategy([\"fa:fa-briefcase JwtStrategy\"]):::provider\n\t\tend\n\t\tsubgraph UserModule\n\t\t\tdirection LR\n\t\t\tUserController{{fa:fa-globe UserController}}:::controller\n\t\t\tUserService([fa:fa-bell-concierge UserService]):::service\n\t\tend\n\t\tsubgraph PoliciesModule\n\t\t\tdirection LR\n\t\t\tCaslAbilityFactory([\"fa:fa-briefcase CaslAbilityFactory\"]):::provider\n\t\tend\n\t\tsubgraph MailerModule\n\t\t\tdirection LR\n\t\t\tMailerService([fa:fa-bell-concierge MailerService]):::service\n\t\tend\n\t\tsubgraph JwtModule[ ]\n      subgraph JwtModulePadding[JwtModule]\n\t\t  end\n\t\tend\n\t\t\n\t\tAppModule===\u003eMongooseModule\n\t\tAppModule===\u003eStandardResponseModule\n\t\tAppModule===\u003eAuthModule\n\t\tAuthModule===\u003eUserModule\n\t\tUserModule-.-\u003eMongooseModule\n\t\tUserModule===\u003ePoliciesModule\n\t\tAuthModule===\u003eMailerModule\n\t\tAuthModule-.-\u003eMongooseModule\n\t\tAppModule===\u003eUserModule\n\t\tAppModule===\u003eMailerModule\n\tend\nclassDef controller fill:darkgreen\nclassDef provider fill:#1f2020\nclassDef service fill:#1f2020\nclassDef pipe fill:#8b0e5d\nclassDef guard fill:#8b0e5d\nclassDef interceptor fill:#8b0e5d\nclassDef model fill:#b83100\nclassDef moduleSubgraph fill:#1f2020,stroke:#81B1DB,rx:5,ry:5\nclassDef globalModule fill:indigo,stroke:#81B1DB,rx:5,ry:5\nclassDef layoutGroup fill:none,stroke:none\nclassDef groupStyles rx:10,ry:10\nclass legend groupStyles\nclass modules,globalModules,legendLine1,legendLine2,JwtModulePadding,MongooseCoreModulePadding,ConfigModulePadding,ConfigHostModulePadding layoutGroup\nclass AppModule,MongooseModule,StandardResponseModule,AuthModule,UserModule,PoliciesModule,MailerModule moduleSubgraph\nstyle legend stroke-dasharray: 0 1 1,fill:white,fill-opacity:0.02,opacity:0.95\n```\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# Models as a Single Source of Truth (SSOT) \u003ca name=\"AboutModels\"\u003e\u003c/a\u003e\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\nModel Classes serve as the unified entry point describing the format and all expectations for a given piece of data. They are used as an `Interface` to create the [mongoose schema](https://mongoosejs.com/docs/typescript/schemas.html), but they are also used to create both ingress and egress DTOs using [Mapped Types](https://docs.nestjs.com/openapi/mapped-types).\n\nThis means the information on Model properties define input validation rules enforced when the model is expected in requests, and defines serialization rules when the model is send in responses.\n\nFinally, model properties can also provide OpenAPI documentation information, like descriptions and usage examples.\n\nHaving all this information present in a central Model Class avoids code duplication, since derivative classes only need to pick what properties of the Model they want, without worrying about providing documentation, examples, validation rules, etc.\n\nThis means that properties on a Model Class can have up to ***4 types*** of decorators on them:\n\n1. ***Schema*** - `@Prop()` from '@nestjs/mongoose' to add the property to the schema;\n2. ***Docs*** - `@ApiProperty()` from '@nestjs/swagger' to add documentation and examples;\n3. ***Serialization*** - `@Exclude()`, `@Expose()`, and `@Transform()` from 'class-transformer' to define serialization rules;\n4. ***Validation*** - `@IsString()`, `@IsEmail()`, `@Min()`, etc... from 'class-validator' to perform input validation;\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003ch3\u003e📚 Example:\u003c/h3\u003e\n\u003c/summary\u003e\n\n```ts\n@Schema() // ⬅ marks a class to be used as the Interface for the mongoose schema\nclass User {\n  @Prop() // ⬅ marks this property to appear in the mongoose schema\n  @ApiProperty({ example: 'Mark' }) // ⬅ provides OpenAPI documentation for this property\n  name: string;\n\n  @Prop({ index: { unique: true } }) // ⬅ accepts the same options as a 'new Mongoose.Schema()'\n  @ApiProperty({ example: 'markhiggens3310@gmail.com' })\n  @IsEmail() // ⬅ provides validation when this property is required as an input\n  email: string;\n\n  // ⬇ will exclude this property on 'output', i.e. from the serialized object sent in responses (but allow it on input)\n  @Exclude({ toPlainOnly: true })\n  @Prop()\n  password: string;\n  \n  // ⬇ will exclude this property on 'input', i.e. from request DTOs and validation (but allow it in responses)\n  @Exclude({ toClassOnly: true })\n  @Prop()\n  lastSeenAt: Date;\n\n  @Exclude() // ⬅ will exclude this property in both directions\n  @Prop()\n  chatAccessKey: string;\n\n  // ⬇ only admins will see this property in the serialized response, it's excluded for everyone else\n  @Expose({ groups: ['Admin'] })\n  @Prop({ type: Date, default: Date.now })\n  @IsDateString()\n  registeredAt: Date;\n\n  // ⬇ allows you to easily create instances of this model from a document from the DB\n  constructor(partial: Partial\u003cUser\u003e = {}) {\n    Object.assign(this, partial);\n  }\n\n  // ⬇ you can add other props and utility methods on the model class\n  hasCake() {\n    const registeredDaysAgo = (new Date().getTime() - this.registeredAt.getTime()) / 1000 / 60 / 60 / 24;\n    return registeredDaysAgo \u003e 365; // 🍰 account is at least one year old!\n  }\n}\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## Sending data\n\nWhen sending data in responses, it's important to always send instances of a Model Class, or instances of DTOs created from it. You can either send a single one, or an array of them. But never send documents retrieved from the database directly in reponses! The serialization rules (and all other benefits from the model) only apply to instances of the Model or derived classes, not documents from the DB.\n\nThis also means you **should not** wrap the returned model in any other javascript object. If you need to add more data to the response (like pagination, filtering, additional messages, etc), you should add them using the metadata decorators provided by `nest-standard-response`.\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003ch3\u003e📚 Example:\u003c/h3\u003e\n\u003c/summary\u003e\n\n```ts\n@Controller('user')\nexport class UserController {\n  @Get()\n  @StandardResponse({ // ⬅ setup a StandardResponse wrapper\n    isPaginated: true,\n  })\n  public async findAll(\n    // ⬇ injects a StandardParam providing methods to manipulate the wrapper\n    @StandardParam() params: StandardParams\n  ): Promise\u003cUser[]\u003e { // ⬅ route return type must always resolve to Model or Model[]\n    const users: UserDocument[] = await this.userModel\n      .find()\n      .limit(params.paginationInfo.limit) // ⬅ we get pagination query params for free\n      .skip(params.paginationInfo.offset) //    by using the isPaginated option above\n      .exec();\n\n    params.setMessage('Custom message...') // ⬅ adds a custom message to the response\n    params.setExtra('myCustomProperty', { // ⬅ add some extra field in the response\n      customObjProp1: 'any serializable value',\n      customObjProp2: { nested: true },\n    });\n    // ⬇ Use the document from the DB to construct a new Model() before returning\n    return users.map((userDoc) =\u003e new User(userDoc.toJSON()));\n  }\n}\n```\n\n### The response from this route would look like this:\n\nNote the smart serialization in the response! The field `registeredAt` is only present when an `admin` is making the request. It would be hidden from other users because of the serialization rules in the model.\n\n```ts\n{\n  success: true,\n  message: \"Custom message...\",\n  isArray: true,\n  isPaginated: true,\n  pagination: {\n    limit: 10,\n    offset: 0,\n    defaultLimit: 10,\n  },\n  myCustomProperty: {\n    customObjProp1: 'any serializable value',\n    customObjProp2: { nested: true },\n  }\n  data: [{\n    name: \"Mark\",\n    email: 'mark8829@gmail.com',\n    registeredAt: '2023-11-09T13:06:37.384Z'\n  }, {\n    name: \"Jane\",\n    email: 'itsmejane@gmail.com',\n    registeredAt: '2023-11-09T13:06:37.384Z'\n  }, {\n    name: \"Eva\",\n    email: 'evanance@hotmail.com' },\n    registeredAt: '2023-11-09T13:06:37.384Z'\n  }]\n}\n```\n\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\n## Receving data\n\nThe same is true for receving data in the request params or body. Always strongly type the expected data as the Model Class or a DTO derived from it. This way the data gets auto validation from the global `ValidationPipe`, plus the route gets auto documentation in Open API.\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003ch3\u003e📚 Example:\u003c/h3\u003e\n\u003c/summary\u003e\n\n```ts\n// CreateUserDto.ts\n\n// ⬇ We choose the properties we want from the model with MappedTypes, so this DTO will inherit all\n// the validation and serialization logic we defined there, without having to duplicate anything\n\nconst requiredFields = ['email', 'password'] as const;\nconst optionalFields = ['name', 'familyName', 'phone', 'birthDate'] as const;\n\nexport class CreateUserDto extends IntersectionType(\n  PartialType(PickType(User, optionalFields)),\n  PickType(User, requiredFields),\n) {}\n\n```\n\n```ts\n@Controller('user')\nexport class UserController {\n  @Post())\n  public async create(\n    // ⬇ Setting our DTO as the Type for the request body means it will be automatically validated\n    // by the global ValidationPipe.\n    @Body() createUserDto: CreateUserDto\n  ): Promise\u003cUser\u003e {\n    // Any request to this route with a body that's missing required fields, or that contains fields\n    // with values that fail the model validation rules will result in a HTTP 400 Bad Request exception,\n    // and this handler will never be executed.\n    // This means it's safe to use body here without any further validation\n    return await this.userService.create(createUserDto);\n  }\n}\n}\n```\n\u003c/details\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n## 🔮 Use concrete JS classes as types, not Typescript interfaces\n\nTypescript interfaces are completely removed from compiled code. Since we want to perform data validation and transformation at runtime, all models and DTOs must use Classes instead. TS Classes can also be used as ***types*** when needed, but they are persisted as JS Classes in the compiled code.\n\n\u003c!-- Most decorators work by using the JS [Reflect API](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) to store metadata inside of those classes. This often includes the ***type*** that those values had in Typescript, but can also include any other value the decorator thinks the app might need at runtime.\n\nAt any point during the app execution, other parts of the app might use [Reflection](https://en.wikipedia.org/wiki/Reflective_programming) to inspect this metadata before deciding on an action. That's how the `ValidationPipe` can inspect a Model or DTO instance and validate the ***type*** of its properties, or how the `RoleSerializerInterceptor` can retrieve the rules used serialize a value. --\u003e\n\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# Auth Module \u003ca name=\"AuthModule\"\u003e\u003c/a\u003e 🚪\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Allows account creation;\n- Sends e-mail verification and keeps track of confirmation status;\n- Sends forgotten password emails and allows password reset;\n- Manages log-in and JWTs;\n- Guard routes from unlogged users and injects the logged-in user into the request.\n\n\u003c/br\u003e\n\u003c/br\u003e\n\n# Policies Module \u003ca name=\"PoliciesModule\"\u003e\u003c/a\u003e 🏛️\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Defines policies limiting any individual user to access only resources they can claim;\n- Claims define which `Actions` (create, read, update, etc...) any user `Role` can take on each `Model`;\n- Claims can also define *constraint queries*, for example allowing a user to read the `User` model, but only for his own user; or to update `Articles`, but only those authored by himself;\n\n\u003e Note: There is no `Articles` module provided by this app. This is just an example on how you can define policies for any model you want.\n\nPolicies are defined using [Casl](https://github.com/stalniy/casl).\n\n\u003cbr /\u003e\n\n## CaslAbilityFactory \u003ca name=\"CaslAbilityFactory\"\u003e\u003c/a\u003e\n\nThe `CaslAbilityFactory` provider exposes the `createForUser` function, which is called during a request with the logged-in user information, and should return a casl `Ability` object constructed using the provided `can` or `cannot` methods. This function is free to inspect the user object and define any custom logic it needs to limit individual access to `actions` taken on `models`.\n\nExample:\n\n```ts\nif (user.roles.includes(UserRole.USER)) {\n  // users can view and update their own info,\n  // view any article, and update articles authored by them\n  can([Action.Read, Action.Update], User, { _id: user._id });\n  can(Action.Read, Article);\n  can(Action.Update, Article, { authorId: user._id });\n}\nif (user.roles.includes(UserRole.MOD)) {\n  // mods can read and update any user or any article\n  can([Action.Read, Action.Update], User);\n  can([Action.Read, Action.Update], Article);\n}\nif (user.roles.includes(UserRole.ADMIN)) {\n  // admins can do anything. Note that 'manage' in casl means all actions,\n  // and the keywork 'all' means in all models. Common actions are 'create',\n  // 'read', 'update', 'delete' and 'list', but you can extend the Actions enum\n  // with any other action you want\n  can(Action.Manage, 'all');\n}\n```\n\n\u003cbr /\u003e\n\n## Protecting routes \u003ca name=\"PoliciesGuard\"\u003e\u003c/a\u003e\u003ca name=\"CheckPoliciesDecorator\"\u003e\u003c/a\u003e\n\nJust add the `PoliciesGuard` to any controller or route. Since policies depend on the user object, using this guard also requires using `AuthGuard` or other mechanism that guarantees log-in.\n\n```ts\n@UseGuards(AuthGuard('jwt'), PoliciesGuard)\n```\n\nOnce this guard is in place, you can add the `@CheckPolicies()` decorator to any route, and choose the claims that are required to access this route. `@CheckPolicies()` expects a simple function that is called with the `userAbility` object, so you can use `can` or `cannot` methods on it to define which Actions this route requires on which Models.\n\n```ts\n@CheckPolicies((ability: UserAbility) =\u003e ability.can(Action.List, User))\n```\n\nChecking policies in this way is very efficient, since requests can be denied at the Guard level, without even executing the route handler. But it is also limited: it cannot check for *constraint queries* since no document has been retrieved from the DB yet. If the logged-in user has access to ***at least one document*** for a given Model, it will be granted access by the guard, and you should check for constraints during the route handling.\n\n\u003cbr /\u003e\n\n## Protecting access per-document \u003ca name=\"UserAbilityParamDecorator\"\u003e\u003c/a\u003e\n\n- The `userAbility` object is also injected in the request object, and you can retrieve it by using `req.userAbility`;\n- If this is all you're using from the request object, it can be cleaner to inject it directly using the custom param decorator `@UserAbilityParam()`;\n\nThis allows you to retrieve documents from the database and call the `can` or `cannot` methods against them. Note that here these methods are called using an instance of the model (instead of on the Model class itself).\n\n```ts\nfunction findOne(\n  @UserAbilityParam() userAbility: UserAbility,\n) {\n  const user = await this.userService.findOne(idOrEmail);\n  if (userAbility.cannot(Action.Read, user)) {\n    throw new ForbiddenException();\n  }\n  return user;\n}\n```\n\u003c/br\u003e\n\n# User Module \u003ca name=\"UserModule\"\u003e\u003c/a\u003e 👤\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Defines the User model, schema and DTOs;\n- Defines the services required to create, read, update, delete, list, reset password, and verify email;\n- Most services from this module are consumed by the Auth module for managing accounts;\n- The user controller provides routes that can be used by admins to manage users from a backend outside of the auth flow;\n- Some routes can also be used by users to view or update their own profile;\n\n\u003cbr /\u003e\n\n## EmailVerifiedGuard \u003ca name=\"EmailVerifiedGuard\"\u003e\u003c/a\u003e\n\nIf the app is configured to use ***delayed*** email verification, users will be logged in automatically after account creation, and will be allowed to login anytime without clicking the verification link.\n\nTo protect access to certain routes only to users who have verified their email, you can add the ```EmailVerifiedGuard``` to any controller or route.\n\n\n```ts\n@UseGuards(EmailVerifiedGuard)\n```\n\n\u003e- If the app is configured to use ***required*** email verification, users will be asked to verified their email before being allowed to log-in. In that case, this guard is redundant.\n\u003e\n\u003e- If the app is configured with email verification ***off***, this guard shoud not be used, since it will never allow access to the routes under it.\n\u003e\n\u003e- The routes from the `UserController` that allow users to view and edit their own information use this guard. If you're setting this setting to ***off***, you should also remove this guard from that controller.\n\n\u003cbr /\u003e\n\n## EmailOrIdPipe \u003ca name=\"EmailOrIdPipe\"\u003e\u003c/a\u003e\n\nBoth `email` and `id` are unique keys in the user schema. An `id` provides consistency since it should never be changed, and also provides some privacy if you need to include a user reference in a public link without exposing their `email`. However, sometimes using an email can be more convenient.\n\nThat's why routes and services from the `User` module accept **both** an `id` or an `email` as the target for their operations. To validate the input parameters in those cases, the app provides the `EmailOrIdPipe` pipe.\n\n```ts\n@Controller('user')\nexport class UserController {\n  constructor(private readonly userService: UserService) {}\n\n  @Get(':idOrEmail')\n  public async findOne(\n    @Param('idOrEmail', EmailOrIdPipe) idOrEmail: string\n  ): Promise\u003cUser\u003e {\n    const user = await this.userService.findOne(idOrEmail);\n    ...\n  }\n}\n```\n\nWhen used, it makes sure the piped data is either a syntactically valid `email` or a syntactically valid `ObjectId`. Note that a pipe can only check for syntax. It will throw a HTTP 400 BadRequestException if the provided information is malformatted, but it's possible that the information is valid yet still doesn't match any known user from the DB.\n\n\u003c/br\u003e\n\u003c/br\u003e\n\n# Mailer Module \u003ca name=\"MailerModule\"\u003e\u003c/a\u003e 📮\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Automatically creates and configures a nodemailer instance using info from the .env file injected by the config module;\n- Defines services for sending emails;\n- Currently this module can send the following emails:\n  - Welcome\n  - Please confirm yout email\n  - Forgot your password?\n  - Your password was reset\n\n\u003c/br\u003e\n\n# Config Module \u003ca name=\"ConfigModule\"\u003e\u003c/a\u003e ⚙️\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Prevents runtime errors by validating environment variables during app startup;\n- Provides helpful console messages when envorinment variables are missing or invalid;\n- Parses `.env` vars into a strongly typed Configuration object that can be dependency injected;\n- Exposes interfaces that can be used to provide types when calling the `configService.get\u003c\u003e()` generic method;\n\nExample:\n```ts\n@Controller('books')\nexport class BooksController {\n  constructor(private readonly configService: ConfigService) {}\n\n  @Get()\n  public async listBooks() {\n    const apiConfig = this.configService.get\u003cApiConfig\u003e('api'); \n    // equivalent to process.env.API_INTERNAL_URL,\n    // but parsed, typed, and guarateed to exist\n    console.log(apiConfig.internalUrl);\n  }\n}\n```\n\n\u003c/br\u003e\n\n# Standard Response Module \u003ca name=\"StandardResponseModule\"\u003e\u003c/a\u003e 📦\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\u003cbr /\u003e\n\n\u003e [StandardReponse](https://github.com/simioni/nest-standard-response) has been exported into a separate package. The full documentation now resides in [it's own repo](https://github.com/simioni/nest-standard-response).\n\n\u003c/br\u003e\n\n* Metadata-based wrapper to provide customizable and standardized API response objects;\n\n* Built-in handling of pagination, sorting and filtering;\n\n* Allows route handlers to keep returning classes instead of wrapper objects, so they remain fully compatible with interceptors;\n\n\u003ctable style=\"width: 100%\"\u003e\n\u003ctr\u003e\n\u003ctd\u003e\n\n```ts\n// 👇 just annotate a route with\n// @StandardResponse() and choose\n// the features you need\n@get(\"/books\")\n@StandardResponse({\n  isPaginated: true,\n  isSorted: true,\n  isFiltered: true,\n})\nasync listBooks(\n  // 👇 then inject a @StandardParam() into\n  // the handler to access the features\n  @StandardParam() params: StandardParams\n): BookDto[] {\n  const {\n    books,\n    count\n  } = await this.bookService.list({\n    // 👇 this route can now be called with\n    // query parameters, fully parsed and\n    // validated to use in services\n    limit: params.pagination.limit,\n    offset: params.pagination.offset,\n    sort: params.pagination.sort,\n    filter: params.pagination.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  // StandardResponse's Docs\n\n  // 👇 add extra information into the response\n  params.setPaginationInfo({ count: count })\n  params.setMessage('Custom message...')\n  return books;\n}\n```\n\n\u003c/td\u003e\n\u003ctd\u003e\n\n```ts\n// response\n{\n  success: true,\n  message: \"Custom message...\",\n  isArray: true,\n  isPaginated: true,\n  isSorted: true,\n  isFiltered: true,\n  pagination: {\n    limit: 10,\n    offset: 0,\n    defaultLimit: 10,\n    // 👇 added in handler\n    count: 33\n  },\n  sorting: {\n    query: ...,\n    sortableFields: [...],\n    sort: SortingInfo\n    // check docs\n  },\n  filtering: {\n    query: ...,\n    filterableFields: [...],\n    filter: FilteringInfo\n    // check docs\n  },\n  data: [\n    { title: \"Dune\", year: 1965 },\n    { title: \"Jaws\", year: 1974 },\n    { title: \"Emma\", year: 1815 },\n  ]\n}\n\n\n\n\n\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\u003c/table\u003e\n\n```ts\n// this route can now be called using query params like this:\n'/books?limit=8\u0026offset=16\u0026sort=-author,title\u0026filter=author^=Frank;year\u003e=1960;year\u003e=1970'\n```\n\nℹ️ Check out the [full documentation](https://github.com/simioni/nest-standard-response) to learn:\n\n- How to [build the query](https://github.com/simioni/nest-standard-response#--building-the-search-query);\n- How the query is parsed: [SortingInfo](https://github.com/simioni/nest-standard-response#--sortinginfo), [FilteringInfo](https://github.com/simioni/nest-standard-response#--filteringinfo) and [PaginationInfo](https://github.com/simioni/nest-standard-response#--paginationinfo);\n- How to use the decorators: [@StandardResponse()](https://github.com/simioni/nest-standard-response#--standardresponseoptions-standardresponseoptions-) and [@StandardParam()](https://github.com/simioni/nest-standard-response#--standardparam-);\n- and other options.\n\n\n\u003c/br\u003e\n\n# Test Module \u003ca name=\"TestModule\"\u003e\u003c/a\u003e 🧪\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n- Provides end-to-end testing of all user interaction flows;\n- e2e tests run in docker, using NODE_ENV=production;\n- Jest runs tests in parallel, so each test file needs to instantiate the app in it's own thread;\n- The DB is shared between all threads. To avoid racing conditions, the DB should never be dropped during testing. Use the provided factories to create and destroy resources instead.\n\nTo facilitate creating and destroing instances of the NestJS application, as well as registering all kinds of test users, this project provides two utility factories:\n\n\u003cbr /\u003e\n\n## *TestingServerFactory* \u003ca name=\"TestingServerFactory\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\nWhen creating new e2e test files, use the `beforeAll` hook from jest to instantiate a new NestJS app by calling `await new TestingServerFactory().create()`.\n\nThis method will create a new *TestingModule*, mock the mailer service, start the app, auto-increment the port number to avoid conflicts, and return an instance with methods to retrieve all created resources, like the `getModule()`, `getApp()` and `getBaseUrl()`.\n\nSince this gives you access to the underlying NestJS *TestingModule*, you can reach any part of the nest app by using the `get()` and `resolve()` methods on the module.\n\n\u003cbr /\u003e\n\n## *UserStubFactory* \u003ca name=\"UserStubFactory\"\u003e\u003c/a\u003e\n\n\u003cbr /\u003e\n\nTo create stub users for testing access control and serialization, use the ***UserStubFactory***. It provides methods for creating regular users, users with verified emails, admin users, etc. It also provides methods to **login** those users and get their access tokens, as well as to **delete** them.\n\nThe test DB is dropped only once before starting e2e tests. It's a good idea to delete any resource you created in the DB during a test inside the `afterAll` hook.\n\nExample:\n\n```ts\ndescribe('BooksController (e2e)', () =\u003e {\n  let app: INestApplication;\n  let stub: UserStubFactory;\n  let verifiedUser: FakeUser;\n  let verifiedUserToken: string;\n  beforeAll(async () =\u003e {\n    const testingServer = await new TestingServerFactory().create();\n    const testingModule = testingServer.getModule();\n    app = testingServer.getApp();\n    booksService = await testingModule.resolve(BooksService);\n\n    stub = new UserStubFactory(testingServer);\n    verifiedUser = await stub.registerNewVerifiedUser({ firstName: 'Martha' });\n    verifiedUserToken = await stub.getLoginTokenForUser(verifiedUser);\n  });\n  afterAll(async () =\u003e {\n    await stub.deleteUser(verifiedUser.email);\n    await app.close();\n  });\n}\n```\n\n\u003c/br\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cbr /\u003e\n\u003cp align=\"right\"\u003e\u003ca href=\"#RefIndex\"\u003e\u003csmall\u003eBack to index \u0026nbsp;⤴\u003c/small\u003e\u003c/a\u003e\u003c/p\u003e\n\n\n---------------------------------------------------\n\n\u003cbr /\u003e\n\n## 🏃 \u0026nbsp; TODO Milestones\n\n\u003c!-- - Add a Redis instance in docker-compose, and:\n  - expose it's config via .env and the config module;\n  - Cache the user from login tokens so we don't need to hit the DB in every request to retrieve it;\n  - Cache the abilities created by casl for a given user so it don't need to be recreated inside the policies every on every request;\n  - share @nestjs/throttler storage in Redis\n--\u003e\n- Add a [mgob](https://github.com/maxisam/mgob) instance to the production docker swarm for automated mongo backups (and add its configurations via .env)\n- Add some tool to the production docker swarm to expose server metrics\n- Add user consent forms with versioned policies\n- Add option for log-in using social media accounts\n\n\u003c!-- \u003cbr /\u003e\n\u003cbr /\u003e\n\n# Motivation\n\nReduces the repetitive task I encounter frequently. It covers the basics to let me focus on building the fun stuff. --\u003e\n\n\u003c!-- A lot of the code in this repo came from my own projects, and it's a culmination of features that I had to implement frequently for different systems.\n\nModern web apps tend to have similar initial demands, so the fastest way to go about starting a new project was copying code around and modifying it a bit. An easy but repetitive task that removes all the joy out of building software.\n\nThis generator provides a way to bootstrap a modern Typescript web app in a way that is not only ***fast***, but more importantly, ***fun***. It covers the basics to let me focus on building the good stuff, the actual features unique to each system.\n\nI hope you can find some fun in it as well. 😊\n\n\u003cbr /\u003e\n\n# Refs\n\nFor consistency, I tried to keep the code as much as posible close to NestJS' code style. This meant taking references from other NestJS open source projects, including:\n\n- [The NestJS source itself](https://github.com/nestjs/nest);\n- [This NestJS' example library](https://github.com/nestjs/nest/tree/master/sample);\n- [golevelup's NestJS collections](https://github.com/golevelup/nestjs);\n- [This NestJS curated list of things](https://github.com/nestjs/awesome-nestjs);\n- [marcomelilli/nestjs-email-authentication](https://github.com/marcomelilli/nestjs-email-authentication);\n- [NarHakobyan/awesome-nest-boilerplate](https://github.com/NarHakobyan/awesome-nest-boilerplate);\n- [nestjsx/crud](https://github.com/nestjsx/crud);\n\n\u003cbr /\u003e --\u003e\n\n\n\u003c!-- --------------------------------------------------------------------------- --\u003e\n\u003c!-- # Motivation\n\nNestJS achieves a great balance between performance, development speed and developer experience. By using TS/JS, it can tap on a vast ecosystem of libraries and tools\n\nThis project:\n- enforces automatic linting and code formatting;\n- is fully tested and encourages test-driven development;\n- provides a \"single source of truth\" architecture for data Models that grant security in operations while avoiding code duplication;\n\n\u003cbr /\u003e\n\n## Model Classes as a \"single source of truth\" for data entities\n\nIn Typescript, data Models and their property types are usually defined as an `Interface` that is implemented by a schema or entity class \n\n- A schema, used by mongoose to provide type safety (and code completion, easier refactoring, etc...)\n  - The schema contains all properties from the model, and represents the data exactly as it is saved in the DB\n- Data validation on input\n  - Contain only the fields that are acceptable for a given operation --\u003e\n\n\u003cbr /\u003e\n\u003cbr /\u003e\n\n# License\n\nMIT License\n\nCopyright (c) 2022 Ricardo Simioni\n\n\u003cbr /\u003e\n\n\u003c!-- \n🏭 ⭐️ 🕹️ 💡 💎 🔩 ⚙️ 🧱 🔮 💈 🛍️ 🎁 🪭 ⚜️ ❇️ 🚩\n📦 🏷️ 📮 \n🟠 🟧 🔶 🔸\n--\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimioni%2Fnest-pret","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimioni%2Fnest-pret","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimioni%2Fnest-pret/lists"}