Repository: HaiderMalik12/nestjs-fundamentals
Branch: main
Commit: 0c095be3c70d
Files: 2870
Total size: 2.0 MB
Directory structure:
gitextract_9ikg04a7/
├── module-02-Creating-REST-APIS/
│ ├── 01-Lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ └── songs.module.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── 02-Lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ └── songs.module.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── 03-Lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ └── songs/
│ │ ├── songs.controller.spec.ts
│ │ ├── songs.controller.ts
│ │ ├── songs.module.ts
│ │ ├── songs.service.spec.ts
│ │ └── songs.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-03-middlewares-exception-filters-pipes/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ └── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ └── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ └── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-04/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── common/
│ │ │ └── middleware/
│ │ │ └── logger.middleware.ts
│ │ ├── main.ts
│ │ └── songs/
│ │ ├── dto/
│ │ │ └── create-song-dto.ts
│ │ ├── songs.controller.spec.ts
│ │ ├── songs.controller.ts
│ │ ├── songs.module.ts
│ │ ├── songs.service.spec.ts
│ │ └── songs.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-04-dependency-injection/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-02/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── main.ts
│ │ └── songs/
│ │ ├── dto/
│ │ │ └── create-song-dto.ts
│ │ ├── songs.controller.spec.ts
│ │ ├── songs.controller.ts
│ │ ├── songs.module.ts
│ │ ├── songs.service.spec.ts
│ │ └── songs.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-05-connect-nestjs-to-postgress/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-04/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── main.ts
│ │ └── songs/
│ │ ├── dto/
│ │ │ ├── create-song-dto.ts
│ │ │ └── update-song-dto.ts
│ │ ├── song.entity.ts
│ │ ├── songs.controller.spec.ts
│ │ ├── songs.controller.ts
│ │ ├── songs.module.ts
│ │ ├── songs.service.spec.ts
│ │ └── songs.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-06-relations/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ └── user.entity.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-02-and-lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ └── artist.entity.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-07-authetication-and-authorization/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ └── auth.service.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ └── dto/
│ │ │ │ └── login.dto.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── login.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ └── jwt-strategy.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-04/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── login.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-05/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-06/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ ├── artist.entity.ts
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ └── artists.service.ts
│ │ ├── auth/
│ │ │ ├── api-key-strategy.ts
│ │ │ ├── artists-jwt-guard.ts
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ ├── login.dto.ts
│ │ │ │ └── validate-token.dto.ts
│ │ │ ├── jwt-guard.ts
│ │ │ ├── jwt-strategy.ts
│ │ │ └── types.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-08-migrations-seeds-debugging/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ └── migrations/
│ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ └── 1685010456982-removed-phone.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ ├── db/
│ │ ├── data-source.ts
│ │ ├── migrations/
│ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ └── 1685010456982-removed-phone.ts
│ │ └── seeds/
│ │ └── seed-data.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ ├── artist.entity.ts
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ └── artists.service.ts
│ │ ├── auth/
│ │ │ ├── api-key-strategy.ts
│ │ │ ├── artists-jwt-guard.ts
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ ├── login.dto.ts
│ │ │ │ └── validate-token.dto.ts
│ │ │ ├── jwt-guard.ts
│ │ │ ├── jwt-strategy.ts
│ │ │ └── types.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── seed/
│ │ │ ├── seed.module.ts
│ │ │ └── seed.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-09-application-configurations/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ │ └── 1685010456982-removed-phone.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ │ └── 1685010456982-removed-phone.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── env.validation.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ ├── db/
│ │ ├── data-source.ts
│ │ ├── migrations/
│ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ └── 1685010456982-removed-phone.ts
│ │ └── seeds/
│ │ └── seed-data.ts
│ ├── env.validation.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ ├── artist.entity.ts
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ └── artists.service.ts
│ │ ├── auth/
│ │ │ ├── api-key-strategy.ts
│ │ │ ├── artists-jwt-guard.ts
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ ├── login.dto.ts
│ │ │ │ └── validate-token.dto.ts
│ │ │ ├── jwt-guard.ts
│ │ │ ├── jwt-strategy.ts
│ │ │ └── types.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── config/
│ │ │ └── configuration.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── seed/
│ │ │ ├── seed.module.ts
│ │ │ └── seed.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── webpack-hmr.config.js
├── module-10-api-documentation-with-swagger/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ │ └── 1685010456982-removed-phone.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── env.validation.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── webpack-hmr.config.js
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ │ └── 1685010456982-removed-phone.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── env.validation.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── webpack-hmr.config.js
│ ├── lesson-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ │ └── 1685010456982-removed-phone.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── env.validation.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── webpack-hmr.config.js
│ └── lesson-04/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ ├── db/
│ │ ├── data-source.ts
│ │ ├── migrations/
│ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ └── 1685010456982-removed-phone.ts
│ │ └── seeds/
│ │ └── seed-data.ts
│ ├── env.validation.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ ├── artist.entity.ts
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ └── artists.service.ts
│ │ ├── auth/
│ │ │ ├── api-key-strategy.ts
│ │ │ ├── artists-jwt-guard.ts
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ ├── login.dto.ts
│ │ │ │ └── validate-token.dto.ts
│ │ │ ├── jwt-guard.ts
│ │ │ ├── jwt-strategy.ts
│ │ │ └── types.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── config/
│ │ │ └── configuration.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── seed/
│ │ │ ├── seed.module.ts
│ │ │ └── seed.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── webpack-hmr.config.js
├── module-11-mongodb/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── docker-compose.yml
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ └── main.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02-and-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── docker-compose.yml
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ └── schemas/
│ │ │ └── song.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-04/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── api.http
│ │ ├── docker-compose.yml
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── schemas/
│ │ │ │ └── song.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-05/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── api.http
│ │ ├── docker-compose.yml
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── schemas/
│ │ │ │ └── song.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-06/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── api.http
│ ├── docker-compose.yml
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── albums/
│ │ │ ├── albums.controller.ts
│ │ │ ├── albums.module.ts
│ │ │ ├── albums.service.ts
│ │ │ ├── dto/
│ │ │ │ └── create-album-dto.ts
│ │ │ └── schemas/
│ │ │ └── album.schema.ts
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ └── songs/
│ │ ├── dto/
│ │ │ └── create-song-dto.ts
│ │ ├── schemas/
│ │ │ └── song.ts
│ │ ├── songs.controller.spec.ts
│ │ ├── songs.controller.ts
│ │ ├── songs.module.ts
│ │ ├── songs.service.spec.ts
│ │ └── songs.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-12-deploy-nestjs/
│ ├── deployment-finish/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── db/
│ │ │ ├── data-source.ts
│ │ │ ├── migrations/
│ │ │ │ └── 1686309549613-init.ts
│ │ │ └── seeds/
│ │ │ └── seed-data.ts
│ │ ├── env.validation.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artist.entity.ts
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ └── artists.service.ts
│ │ │ ├── auth/
│ │ │ │ ├── api-key-strategy.ts
│ │ │ │ ├── artists-jwt-guard.ts
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ ├── login.dto.ts
│ │ │ │ │ └── validate-token.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ ├── jwt-strategy.ts
│ │ │ │ └── types.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── config/
│ │ │ │ └── configuration.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── seed/
│ │ │ │ ├── seed.module.ts
│ │ │ │ └── seed.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ ├── tsconfig.json
│ │ └── webpack-hmr.config.js
│ └── deployment-starter/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ ├── db/
│ │ ├── data-source.ts
│ │ ├── migrations/
│ │ │ ├── 1685010320827-my-migrations.ts
│ │ │ └── 1685010456982-removed-phone.ts
│ │ └── seeds/
│ │ └── seed-data.ts
│ ├── env.validation.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ ├── artist.entity.ts
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ └── artists.service.ts
│ │ ├── auth/
│ │ │ ├── api-key-strategy.ts
│ │ │ ├── artists-jwt-guard.ts
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ ├── login.dto.ts
│ │ │ │ └── validate-token.dto.ts
│ │ │ ├── jwt-guard.ts
│ │ │ ├── jwt-strategy.ts
│ │ │ └── types.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── config/
│ │ │ └── configuration.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── seed/
│ │ │ ├── seed.module.ts
│ │ │ └── seed.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ ├── tsconfig.json
│ └── webpack-hmr.config.js
├── module-13-testing/
│ ├── end-to-end-testing/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.module.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── songs/
│ │ │ └── songs.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── jest-basics/
│ │ ├── .prettierrc
│ │ ├── mock-function.spec.js
│ │ ├── package.json
│ │ ├── spyon-demon.spec.js
│ │ ├── sum.js
│ │ └── sum.test.js
│ ├── unit-test-controller-and-service/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.module.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── unit-test-setup/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ └── song/
│ │ ├── dto/
│ │ │ ├── create-song-dto.ts
│ │ │ └── update-song-dto.ts
│ │ ├── song.controller.spec.ts
│ │ ├── song.controller.ts
│ │ ├── song.entity.ts
│ │ ├── song.module.ts
│ │ ├── song.service.spec.ts
│ │ └── song.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-14-websocket/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ └── main.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── lesson-01/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── .prettierrc
│ │ │ ├── README.md
│ │ │ ├── nest-cli.json
│ │ │ ├── package.json
│ │ │ ├── src/
│ │ │ │ ├── app.controller.spec.ts
│ │ │ │ ├── app.controller.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.service.ts
│ │ │ │ └── main.ts
│ │ │ ├── test/
│ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ └── jest-e2e.json
│ │ │ ├── tsconfig.build.json
│ │ │ └── tsconfig.json
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── events/
│ │ │ │ ├── events.gateway.spec.ts
│ │ │ │ ├── events.gateway.ts
│ │ │ │ └── events.module.ts
│ │ │ └── main.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── settings.json
│ ├── README.md
│ ├── client/
│ │ └── index.html
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── events/
│ │ │ ├── events.gateway.spec.ts
│ │ │ ├── events.gateway.ts
│ │ │ └── events.module.ts
│ │ └── main.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-15-build-graphql-apis/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-03-and-04/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.resolver.spec.ts
│ │ │ ├── song.resolver.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-05/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── generate-typings.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── graphql.ts
│ │ ├── main.ts
│ │ └── song/
│ │ ├── dto/
│ │ │ ├── create-song-dto.ts
│ │ │ └── update-song-dto.ts
│ │ ├── song.controller.spec.ts
│ │ ├── song.controller.ts
│ │ ├── song.entity.ts
│ │ ├── song.graphql
│ │ ├── song.module.ts
│ │ ├── song.resolver.spec.ts
│ │ ├── song.resolver.ts
│ │ ├── song.service.spec.ts
│ │ └── song.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-16-authenticate-graphql-apis/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.graphql
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── login.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ └── jwt-strategy.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── lesson-01/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── .prettierrc
│ │ │ ├── .vscode/
│ │ │ │ └── launch.json
│ │ │ ├── README.md
│ │ │ ├── generate-typings.ts
│ │ │ ├── nest-cli.json
│ │ │ ├── package.json
│ │ │ ├── rest-client.http
│ │ │ ├── src/
│ │ │ │ ├── app.controller.spec.ts
│ │ │ │ ├── app.controller.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.service.ts
│ │ │ │ ├── artists/
│ │ │ │ │ └── artist.entity.ts
│ │ │ │ ├── auth/
│ │ │ │ │ ├── auth.constants.ts
│ │ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ │ ├── auth.controller.ts
│ │ │ │ │ ├── auth.graphql
│ │ │ │ │ ├── auth.module.ts
│ │ │ │ │ ├── auth.service.spec.ts
│ │ │ │ │ ├── auth.service.ts
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── login.dto.ts
│ │ │ │ │ ├── jwt-guard.ts
│ │ │ │ │ └── jwt-strategy.ts
│ │ │ │ ├── common/
│ │ │ │ │ ├── constatnts/
│ │ │ │ │ │ └── connection.ts
│ │ │ │ │ ├── middleware/
│ │ │ │ │ │ └── logger.middleware.ts
│ │ │ │ │ └── providers/
│ │ │ │ │ └── DevConfigService.ts
│ │ │ │ ├── graphql.ts
│ │ │ │ ├── main.ts
│ │ │ │ ├── playlists/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ │ ├── playlist.entity.ts
│ │ │ │ │ ├── playlists.controller.ts
│ │ │ │ │ ├── playlists.module.ts
│ │ │ │ │ └── playlists.service.ts
│ │ │ │ ├── songs/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ │ └── update-song-dto.ts
│ │ │ │ │ ├── song.entity.ts
│ │ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ │ ├── songs.controller.ts
│ │ │ │ │ ├── songs.module.ts
│ │ │ │ │ ├── songs.service.spec.ts
│ │ │ │ │ └── songs.service.ts
│ │ │ │ └── users/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-user.dto.ts
│ │ │ │ ├── user.entity.ts
│ │ │ │ ├── users.module.ts
│ │ │ │ ├── users.service.spec.ts
│ │ │ │ └── users.service.ts
│ │ │ ├── test/
│ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ └── jest-e2e.json
│ │ │ ├── tsconfig.build.json
│ │ │ └── tsconfig.json
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.graphql
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.resolver.spec.ts
│ │ │ │ ├── auth.resolver.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── login.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ └── jwt-strategy.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-03/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── .vscode/
│ │ └── launch.json
│ ├── README.md
│ ├── generate-typings.ts
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── .vscode/
│ │ │ └── launch.json
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── lesson-01/
│ │ │ ├── .eslintrc.js
│ │ │ ├── .gitignore
│ │ │ ├── .prettierrc
│ │ │ ├── .vscode/
│ │ │ │ └── launch.json
│ │ │ ├── README.md
│ │ │ ├── generate-typings.ts
│ │ │ ├── nest-cli.json
│ │ │ ├── package.json
│ │ │ ├── rest-client.http
│ │ │ ├── src/
│ │ │ │ ├── app.controller.spec.ts
│ │ │ │ ├── app.controller.ts
│ │ │ │ ├── app.module.ts
│ │ │ │ ├── app.service.ts
│ │ │ │ ├── artists/
│ │ │ │ │ └── artist.entity.ts
│ │ │ │ ├── auth/
│ │ │ │ │ ├── auth.constants.ts
│ │ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ │ ├── auth.controller.ts
│ │ │ │ │ ├── auth.graphql
│ │ │ │ │ ├── auth.module.ts
│ │ │ │ │ ├── auth.service.spec.ts
│ │ │ │ │ ├── auth.service.ts
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── login.dto.ts
│ │ │ │ │ ├── jwt-guard.ts
│ │ │ │ │ └── jwt-strategy.ts
│ │ │ │ ├── common/
│ │ │ │ │ ├── constatnts/
│ │ │ │ │ │ └── connection.ts
│ │ │ │ │ ├── middleware/
│ │ │ │ │ │ └── logger.middleware.ts
│ │ │ │ │ └── providers/
│ │ │ │ │ └── DevConfigService.ts
│ │ │ │ ├── graphql.ts
│ │ │ │ ├── main.ts
│ │ │ │ ├── playlists/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ │ ├── playlist.entity.ts
│ │ │ │ │ ├── playlists.controller.ts
│ │ │ │ │ ├── playlists.module.ts
│ │ │ │ │ └── playlists.service.ts
│ │ │ │ ├── songs/
│ │ │ │ │ ├── dto/
│ │ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ │ └── update-song-dto.ts
│ │ │ │ │ ├── song.entity.ts
│ │ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ │ ├── songs.controller.ts
│ │ │ │ │ ├── songs.module.ts
│ │ │ │ │ ├── songs.service.spec.ts
│ │ │ │ │ └── songs.service.ts
│ │ │ │ └── users/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-user.dto.ts
│ │ │ │ ├── user.entity.ts
│ │ │ │ ├── users.module.ts
│ │ │ │ ├── users.service.spec.ts
│ │ │ │ └── users.service.ts
│ │ │ ├── test/
│ │ │ │ ├── app.e2e-spec.ts
│ │ │ │ └── jest-e2e.json
│ │ │ ├── tsconfig.build.json
│ │ │ └── tsconfig.json
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── rest-client.http
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ └── artist.entity.ts
│ │ │ ├── auth/
│ │ │ │ ├── auth.constants.ts
│ │ │ │ ├── auth.controller.spec.ts
│ │ │ │ ├── auth.controller.ts
│ │ │ │ ├── auth.graphql
│ │ │ │ ├── auth.module.ts
│ │ │ │ ├── auth.resolver.spec.ts
│ │ │ │ ├── auth.resolver.ts
│ │ │ │ ├── auth.service.spec.ts
│ │ │ │ ├── auth.service.ts
│ │ │ │ ├── dto/
│ │ │ │ │ └── login.dto.ts
│ │ │ │ ├── jwt-guard.ts
│ │ │ │ └── jwt-strategy.ts
│ │ │ ├── common/
│ │ │ │ ├── constatnts/
│ │ │ │ │ └── connection.ts
│ │ │ │ ├── middleware/
│ │ │ │ │ └── logger.middleware.ts
│ │ │ │ └── providers/
│ │ │ │ └── DevConfigService.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ ├── playlists/
│ │ │ │ ├── dto/
│ │ │ │ │ └── create-playlist.dto.ts
│ │ │ │ ├── playlist.entity.ts
│ │ │ │ ├── playlists.controller.ts
│ │ │ │ ├── playlists.module.ts
│ │ │ │ └── playlists.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song-dto.ts
│ │ │ │ │ └── update-song-dto.ts
│ │ │ │ ├── song.entity.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ └── create-user.dto.ts
│ │ │ ├── user.entity.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── nest-cli.json
│ ├── package.json
│ ├── rest-client.http
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── artists/
│ │ │ └── artist.entity.ts
│ │ ├── auth/
│ │ │ ├── auth.constants.ts
│ │ │ ├── auth.controller.spec.ts
│ │ │ ├── auth.controller.ts
│ │ │ ├── auth.graphql
│ │ │ ├── auth.module.ts
│ │ │ ├── auth.resolver.spec.ts
│ │ │ ├── auth.resolver.ts
│ │ │ ├── auth.service.spec.ts
│ │ │ ├── auth.service.ts
│ │ │ ├── dto/
│ │ │ │ └── login.dto.ts
│ │ │ ├── gql-auth-guard.ts
│ │ │ ├── jwt-guard.ts
│ │ │ └── jwt-strategy.ts
│ │ ├── common/
│ │ │ ├── constatnts/
│ │ │ │ └── connection.ts
│ │ │ ├── middleware/
│ │ │ │ └── logger.middleware.ts
│ │ │ └── providers/
│ │ │ └── DevConfigService.ts
│ │ ├── graphql.ts
│ │ ├── main.ts
│ │ ├── playlists/
│ │ │ ├── dto/
│ │ │ │ └── create-playlist.dto.ts
│ │ │ ├── playlist.entity.ts
│ │ │ ├── playlists.controller.ts
│ │ │ ├── playlists.module.ts
│ │ │ └── playlists.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ └── create-user.dto.ts
│ │ ├── user.entity.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-17-subscription/
│ ├── subscription-finish/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.resolver.spec.ts
│ │ │ ├── song.resolver.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── subscription-starter/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── generate-typings.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── graphql.ts
│ │ ├── main.ts
│ │ └── song/
│ │ ├── dto/
│ │ │ ├── create-song-dto.ts
│ │ │ └── update-song-dto.ts
│ │ ├── song.controller.spec.ts
│ │ ├── song.controller.ts
│ │ ├── song.entity.ts
│ │ ├── song.graphql
│ │ ├── song.module.ts
│ │ ├── song.resolver.spec.ts
│ │ ├── song.resolver.ts
│ │ ├── song.service.spec.ts
│ │ └── song.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-18-testing-graphql-apis/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.resolver.spec.ts
│ │ │ ├── song.resolver.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-02/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── generate-typings.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── graphql.ts
│ │ ├── main.ts
│ │ └── song/
│ │ ├── dto/
│ │ │ ├── create-song-dto.ts
│ │ │ └── update-song-dto.ts
│ │ ├── song.controller.spec.ts
│ │ ├── song.controller.ts
│ │ ├── song.entity.ts
│ │ ├── song.graphql
│ │ ├── song.module.ts
│ │ ├── song.resolver.spec.ts
│ │ ├── song.resolver.ts
│ │ ├── song.service.spec.ts
│ │ └── song.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ ├── jest-e2e.json
│ │ └── song/
│ │ └── song.e2e-spec.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-19-graphql-advanced-concepts/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── generate-typings.ts
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── graphql.ts
│ │ │ ├── main.ts
│ │ │ └── song/
│ │ │ ├── dto/
│ │ │ │ ├── create-song-dto.ts
│ │ │ │ └── update-song-dto.ts
│ │ │ ├── song.controller.spec.ts
│ │ │ ├── song.controller.ts
│ │ │ ├── song.entity.ts
│ │ │ ├── song.graphql
│ │ │ ├── song.module.ts
│ │ │ ├── song.resolver.spec.ts
│ │ │ ├── song.resolver.ts
│ │ │ ├── song.service.spec.ts
│ │ │ └── song.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02-dataloaders/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── src/
│ │ │ ├── app.module.ts
│ │ │ ├── main.ts
│ │ │ ├── posts/
│ │ │ │ ├── post.entity.ts
│ │ │ │ ├── posts.module.ts
│ │ │ │ ├── posts.resolver.ts
│ │ │ │ └── posts.service.ts
│ │ │ ├── schema.gql
│ │ │ ├── users/
│ │ │ │ ├── user.entity.ts
│ │ │ │ ├── users.loader.ts
│ │ │ │ ├── users.module.ts
│ │ │ │ ├── users.resolver.ts
│ │ │ │ └── users.service.ts
│ │ │ └── util.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── jest-e2e.json
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-03-fetching-external-api/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── api.http
│ ├── docker-compose.yml
│ ├── generate-typings.ts
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── album/
│ │ │ ├── album.controller.ts
│ │ │ ├── album.module.ts
│ │ │ ├── album.service.ts
│ │ │ ├── dto/
│ │ │ │ └── create-album-dto.ts
│ │ │ └── schemas/
│ │ │ └── album.schema.ts
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── graphql.ts
│ │ ├── main.ts
│ │ ├── product/
│ │ │ ├── product-owner.resolver.ts
│ │ │ ├── product.graphql
│ │ │ ├── product.module.ts
│ │ │ ├── product.resolver.spec.ts
│ │ │ ├── product.resolver.ts
│ │ │ ├── product.service.spec.ts
│ │ │ ├── product.service.ts
│ │ │ └── schemas/
│ │ │ └── product.schema.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ └── create-song-dto.ts
│ │ │ ├── schemas/
│ │ │ │ └── song.schema.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── todo/
│ │ │ ├── todo.graphql
│ │ │ ├── todo.module.ts
│ │ │ ├── todo.resolver.spec.ts
│ │ │ ├── todo.resolver.ts
│ │ │ ├── todo.service.spec.ts
│ │ │ └── todo.service.ts
│ │ └── user/
│ │ ├── schemas/
│ │ │ └── user.schema.ts
│ │ ├── user.module.ts
│ │ └── user.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── module-20-prisma-integration/
│ ├── lesson-01/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ └── main.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-02/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ └── main.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-03/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ └── prisma.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-04-and-05/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── http-client.http
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── main.ts
│ │ │ ├── prisma.service.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song.dto.ts
│ │ │ │ └── update-song.dto.ts
│ │ │ ├── entities/
│ │ │ │ └── song.entity.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-06/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── http-client.http
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801082432_add_artists/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ ├── artists.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-artist.dto.ts
│ │ │ │ └── update-artist.dto.ts
│ │ │ ├── main.ts
│ │ │ ├── prisma.service.ts
│ │ │ └── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song.dto.ts
│ │ │ │ └── update-song.dto.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-07/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── http-client.http
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801082432_add_artists/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801091013_one_to_one/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── artists/
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ ├── artists.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-artist.dto.ts
│ │ │ │ └── update-artist.dto.ts
│ │ │ ├── main.ts
│ │ │ ├── prisma.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song.dto.ts
│ │ │ │ │ └── update-song.dto.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ ├── create-user.dto.ts
│ │ │ │ └── update-user.dto.ts
│ │ │ ├── users.controller.spec.ts
│ │ │ ├── users.controller.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-08/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── http-client.http
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801082432_add_artists/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801091013_one_to_one/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802074328_many_to_many/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802081727_nested_queries/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802083400_default_value_for_type/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── applications/
│ │ │ │ ├── applications.controller.spec.ts
│ │ │ │ ├── applications.controller.ts
│ │ │ │ ├── applications.module.ts
│ │ │ │ ├── applications.service.spec.ts
│ │ │ │ ├── applications.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-application.dto.ts
│ │ │ │ └── update-application.dto.ts
│ │ │ ├── artists/
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ ├── artists.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-artist.dto.ts
│ │ │ │ └── update-artist.dto.ts
│ │ │ ├── main.ts
│ │ │ ├── posts/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-post.dto.ts
│ │ │ │ │ └── update-post.dto.ts
│ │ │ │ ├── posts.controller.spec.ts
│ │ │ │ ├── posts.controller.ts
│ │ │ │ ├── posts.module.ts
│ │ │ │ ├── posts.service.spec.ts
│ │ │ │ └── posts.service.ts
│ │ │ ├── prisma.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song.dto.ts
│ │ │ │ │ └── update-song.dto.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ ├── create-user.dto.ts
│ │ │ │ └── update-user.dto.ts
│ │ │ ├── users.controller.spec.ts
│ │ │ ├── users.controller.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ ├── lesson-09/
│ │ ├── .eslintrc.js
│ │ ├── .gitignore
│ │ ├── .prettierrc
│ │ ├── README.md
│ │ ├── http-client.http
│ │ ├── nest-cli.json
│ │ ├── package.json
│ │ ├── prisma/
│ │ │ ├── migrations/
│ │ │ │ ├── 20230730081110_init/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801082432_add_artists/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230801091013_one_to_one/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802074328_many_to_many/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802081727_nested_queries/
│ │ │ │ │ └── migration.sql
│ │ │ │ ├── 20230802083400_default_value_for_type/
│ │ │ │ │ └── migration.sql
│ │ │ │ └── migration_lock.toml
│ │ │ └── schema.prisma
│ │ ├── src/
│ │ │ ├── app.controller.spec.ts
│ │ │ ├── app.controller.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.service.ts
│ │ │ ├── applications/
│ │ │ │ ├── applications.controller.spec.ts
│ │ │ │ ├── applications.controller.ts
│ │ │ │ ├── applications.module.ts
│ │ │ │ ├── applications.service.spec.ts
│ │ │ │ ├── applications.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-application.dto.ts
│ │ │ │ └── update-application.dto.ts
│ │ │ ├── artists/
│ │ │ │ ├── artists.controller.spec.ts
│ │ │ │ ├── artists.controller.ts
│ │ │ │ ├── artists.module.ts
│ │ │ │ ├── artists.service.spec.ts
│ │ │ │ ├── artists.service.ts
│ │ │ │ └── dto/
│ │ │ │ ├── create-artist.dto.ts
│ │ │ │ └── update-artist.dto.ts
│ │ │ ├── main.ts
│ │ │ ├── posts/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-post.dto.ts
│ │ │ │ │ └── update-post.dto.ts
│ │ │ │ ├── posts.controller.spec.ts
│ │ │ │ ├── posts.controller.ts
│ │ │ │ ├── posts.module.ts
│ │ │ │ ├── posts.service.spec.ts
│ │ │ │ └── posts.service.ts
│ │ │ ├── prisma.service.ts
│ │ │ ├── songs/
│ │ │ │ ├── dto/
│ │ │ │ │ ├── create-song.dto.ts
│ │ │ │ │ └── update-song.dto.ts
│ │ │ │ ├── songs.controller.spec.ts
│ │ │ │ ├── songs.controller.ts
│ │ │ │ ├── songs.module.ts
│ │ │ │ ├── songs.service.spec.ts
│ │ │ │ └── songs.service.ts
│ │ │ └── users/
│ │ │ ├── dto/
│ │ │ │ ├── create-user.dto.ts
│ │ │ │ └── update-user.dto.ts
│ │ │ ├── users.controller.spec.ts
│ │ │ ├── users.controller.ts
│ │ │ ├── users.module.ts
│ │ │ ├── users.service.spec.ts
│ │ │ └── users.service.ts
│ │ ├── test/
│ │ │ ├── app.e2e-spec.ts
│ │ │ ├── jest-e2e.json
│ │ │ └── song/
│ │ │ └── song.e2e-spec.ts
│ │ ├── tsconfig.build.json
│ │ └── tsconfig.json
│ └── lesson-11-interactive-transactions/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── prisma/
│ │ ├── migrations/
│ │ │ ├── 20230730081110_init/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230801082432_add_artists/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230801091013_one_to_one/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230802074328_many_to_many/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230802081727_nested_queries/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230802083400_default_value_for_type/
│ │ │ │ └── migration.sql
│ │ │ ├── 20230804090722_add_account/
│ │ │ │ └── migration.sql
│ │ │ └── migration_lock.toml
│ │ └── schema.prisma
│ ├── src/
│ │ ├── accounts/
│ │ │ ├── accounts.controller.spec.ts
│ │ │ ├── accounts.controller.ts
│ │ │ ├── accounts.module.ts
│ │ │ ├── accounts.service.spec.ts
│ │ │ ├── accounts.service.ts
│ │ │ └── dto/
│ │ │ ├── create-account.dto.ts
│ │ │ ├── transfer-account.dto.ts
│ │ │ └── update-account.dto.ts
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── applications/
│ │ │ ├── applications.controller.spec.ts
│ │ │ ├── applications.controller.ts
│ │ │ ├── applications.module.ts
│ │ │ ├── applications.service.spec.ts
│ │ │ ├── applications.service.ts
│ │ │ └── dto/
│ │ │ ├── create-application.dto.ts
│ │ │ └── update-application.dto.ts
│ │ ├── artists/
│ │ │ ├── artists.controller.spec.ts
│ │ │ ├── artists.controller.ts
│ │ │ ├── artists.module.ts
│ │ │ ├── artists.service.spec.ts
│ │ │ ├── artists.service.ts
│ │ │ └── dto/
│ │ │ ├── create-artist.dto.ts
│ │ │ └── update-artist.dto.ts
│ │ ├── main.ts
│ │ ├── posts/
│ │ │ ├── dto/
│ │ │ │ ├── create-post.dto.ts
│ │ │ │ └── update-post.dto.ts
│ │ │ ├── posts.controller.spec.ts
│ │ │ ├── posts.controller.ts
│ │ │ ├── posts.module.ts
│ │ │ ├── posts.service.spec.ts
│ │ │ └── posts.service.ts
│ │ ├── prisma.service.ts
│ │ ├── songs/
│ │ │ ├── dto/
│ │ │ │ ├── create-song.dto.ts
│ │ │ │ └── update-song.dto.ts
│ │ │ ├── songs.controller.spec.ts
│ │ │ ├── songs.controller.ts
│ │ │ ├── songs.module.ts
│ │ │ ├── songs.service.spec.ts
│ │ │ └── songs.service.ts
│ │ └── users/
│ │ ├── dto/
│ │ │ ├── create-user.dto.ts
│ │ │ └── update-user.dto.ts
│ │ ├── users.controller.spec.ts
│ │ ├── users.controller.ts
│ │ ├── users.module.ts
│ │ ├── users.service.spec.ts
│ │ └── users.service.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ ├── jest-e2e.json
│ │ └── song/
│ │ └── song.e2e-spec.ts
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── module-21-nestjs-advanced-concepts/
├── 0-starter/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ └── main.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-01-file-upload/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ └── main.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-02-custom-decorator/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-03-Task-scheduling/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ ├── task/
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-04-cookies/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ ├── task/
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-05-queues/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── main.ts
│ │ ├── task/
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-06-event-emitter/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── docker-compose.yml
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── audio/
│ │ │ ├── audio-converted-listener.ts
│ │ │ ├── audio.controller.spec.ts
│ │ │ ├── audio.controller.ts
│ │ │ ├── audio.module.ts
│ │ │ ├── audio.processor.ts
│ │ │ └── events/
│ │ │ └── audio-converted-event.ts
│ │ ├── main.ts
│ │ ├── task/
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── lesson-07-streaming/
│ ├── .eslintrc.js
│ ├── .gitignore
│ ├── .prettierrc
│ ├── README.md
│ ├── docker-compose.yml
│ ├── http-client.http
│ ├── nest-cli.json
│ ├── package.json
│ ├── src/
│ │ ├── app.controller.spec.ts
│ │ ├── app.controller.ts
│ │ ├── app.module.ts
│ │ ├── app.service.ts
│ │ ├── audio/
│ │ │ ├── audio-converted-listener.ts
│ │ │ ├── audio.controller.spec.ts
│ │ │ ├── audio.controller.ts
│ │ │ ├── audio.module.ts
│ │ │ ├── audio.processor.ts
│ │ │ └── events/
│ │ │ └── audio-converted-event.ts
│ │ ├── file/
│ │ │ ├── file.controller.spec.ts
│ │ │ └── file.controller.ts
│ │ ├── main.ts
│ │ ├── task/
│ │ │ ├── task.service.spec.ts
│ │ │ └── task.service.ts
│ │ ├── user.decorator.ts
│ │ └── user.entity.ts
│ ├── test/
│ │ ├── app.e2e-spec.ts
│ │ └── jest-e2e.json
│ ├── tsconfig.build.json
│ └── tsconfig.json
└── lesson-08-session/
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── README.md
├── docker-compose.yml
├── http-client.http
├── nest-cli.json
├── package.json
├── src/
│ ├── app.controller.spec.ts
│ ├── app.controller.ts
│ ├── app.module.ts
│ ├── app.service.ts
│ ├── audio/
│ │ ├── audio-converted-listener.ts
│ │ ├── audio.controller.spec.ts
│ │ ├── audio.controller.ts
│ │ ├── audio.module.ts
│ │ ├── audio.processor.ts
│ │ └── events/
│ │ └── audio-converted-event.ts
│ ├── file/
│ │ ├── file.controller.spec.ts
│ │ └── file.controller.ts
│ ├── main.ts
│ ├── task/
│ │ ├── task.service.spec.ts
│ │ └── task.service.ts
│ ├── user.decorator.ts
│ └── user.entity.ts
├── test/
│ ├── app.e2e-spec.ts
│ └── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
================================================
FILE CONTENTS
================================================
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/app.module.ts
================================================
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
@Module({})
export class SongsModule {}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-02-Creating-REST-APIS/01-Lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/app.module.ts
================================================
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/songs/songs.controller.ts
================================================
import { Controller, Get, Put, Delete, Post } from '@nestjs/common';
@Controller('songs')
export class SongsController {
@Post()
create() {
return 'create a new song endpoint';
}
@Get()
findAll() {
return 'find all songs endpoint';
}
@Get(':id')
findOne() {
return 'fetch song on the based on id';
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
@Module({
controllers: [SongsController]
})
export class SongsModule {}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-02-Creating-REST-APIS/02-Lesson-02/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/app.module.ts
================================================
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/songs/songs.controller.ts
================================================
import { Controller, Get, Put, Delete, Post } from '@nestjs/common';
import { SongsService } from './songs.service';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create() {
return this.songsService.create('Animals by Martin Garrix');
}
@Get()
findAll() {
return this.songsService.findAll();
}
@Get(':id')
findOne() {
return 'fetch song on the based on id';
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService]
})
export class SongsModule {}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
return this.songs;
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-02-Creating-REST-APIS/03-Lesson-03/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/app.module.ts
================================================
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/songs/songs.controller.ts
================================================
import { Controller, Get, Put, Delete, Post } from '@nestjs/common';
import { SongsService } from './songs.service';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create() {
return this.songsService.create('Animals by Martin Garrix');
}
@Get()
findAll() {
return this.songsService.findAll();
}
@Get(':id')
findOne() {
return 'fetch song on the based on id';
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService]
})
export class SongsModule {}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
return this.songs;
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/app.module.ts
================================================
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { SongsService } from './songs.service';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create() {
return this.songsService.create('Animals by Martin Garrix');
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne() {
return 'fetch song on the based on id';
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService]
})
export class SongsModule {}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-02/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/app.module.ts
================================================
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create() {
return this.songsService.create('Animals by Martin Garrix');
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService]
})
export class SongsModule {}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-03/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3001/songs
Content-Type: application/json
{
"title": "lasting lover",
"artists": ["sigla"],
"releasedDate" : "2022-09-29",
"duration" :"02:34"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/app.module.ts
================================================
import {
MiddlewareConsumer,
Module,
NestModule,
RequestMethod,
} from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/app.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title: string;
@IsNotEmpty()
@IsArray()
@IsString()
readonly artists: string[];
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO) {
return this.songsService.create(createSongDTO);
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService]
})
export class SongsModule {}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-03-middlewares-exception-filters-pipes/lesson-04/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-04-dependency-injection/lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-04-dependency-injection/lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-04-dependency-injection/lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-04-dependency-injection/lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "lasting lover",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2022-09-29",
"duration" :"02:34"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-04-dependency-injection/lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-04-dependency-injection/lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { DevConfigService } from './common/providers/DevConfigService';
const devConfig = { port: 3000 };
const proConfig = { port: 4000 };
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [
AppService,
{
provide: DevConfigService,
useClass: DevConfigService,
},
{
provide: 'CONFIG',
useFactory: () => {
return process.env.NODE_ENV === 'development' ? devConfig : proConfig;
},
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
constructor(
private devConfigService: DevConfigService,
@Inject('CONFIG')
private config: { port: string },
) {}
getHello(): string {
return `Hello I am learning Nest.js Fundamentals ${this.devConfigService.getDBHOST()} PORT = ${
this.config.port
}`;
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-04-dependency-injection/lesson-01/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Connection } from 'src/common/constatnts/connection';
@Controller('songs')
export class SongsController {
constructor(
private songsService: SongsService,
@Inject('CONNECTION')
private connection: Connection,
) {
console.log(
`THIS IS CONNECTION STRING ${this.connection.CONNECTION_STRING}`,
);
}
@Post()
create(@Body() createSongDTO: CreateSongDTO) {
return this.songsService.create(createSongDTO);
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { connection } from 'src/common/constatnts/connection';
const mockSongsService = {
findAll() {
return [{ id: 1, title: 'Lasting lover', artists: ['Siagla'] }];
},
};
@Module({
controllers: [SongsController],
providers: [
SongsService,
// {
// provide: SongsService,
// useClass: SongsService,
// },
// {
// provide: SongsService,
// useValue: mockSongsService,
// },
{
provide: 'CONNECTION',
useValue: connection,
},
],
})
export class SongsModule {}
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-04-dependency-injection/lesson-01/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
// throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-04-dependency-injection/lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-04-dependency-injection/lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-04-dependency-injection/lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-04-dependency-injection/lesson-02/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-04-dependency-injection/lesson-02/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-04-dependency-injection/lesson-02/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-04-dependency-injection/lesson-02/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "lasting lover",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2022-09-29",
"duration" :"02:34"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-04-dependency-injection/lesson-02/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-04-dependency-injection/lesson-02/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { DevConfigService } from './common/providers/DevConfigService';
const devConfig = { port: 3000 };
const proConfig = { port: 4000 };
@Module({
imports: [SongsModule],
controllers: [AppController],
providers: [
AppService,
{
provide: DevConfigService,
useClass: DevConfigService,
},
{
provide: 'CONFIG',
useFactory: () => {
return process.env.NODE_ENV === 'development' ? devConfig : proConfig;
},
},
],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
constructor(
private devConfigService: DevConfigService,
@Inject('CONFIG')
private config: { port: string },
) {}
getHello(): string {
return `Hello I am learning Nest.js Fundamentals ${this.devConfigService.getDBHOST()} PORT = ${
this.config.port
}`;
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-04-dependency-injection/lesson-02/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Connection } from 'src/common/constatnts/connection';
@Controller({ path: 'songs', scope: Scope.REQUEST })
export class SongsController {
constructor(
private songsService: SongsService,
@Inject('CONNECTION')
private connection: Connection,
) {
console.log(
`THIS IS CONNECTION STRING ${this.connection.CONNECTION_STRING}`,
);
}
@Post()
create(@Body() createSongDTO: CreateSongDTO) {
return this.songsService.create(createSongDTO);
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { connection } from 'src/common/constatnts/connection';
const mockSongsService = {
findAll() {
return [{ id: 1, title: 'Lasting lover', artists: ['Siagla'] }];
},
};
@Module({
controllers: [SongsController],
providers: [
SongsService,
// {
// provide: SongsService,
// useClass: SongsService,
// },
// {
// provide: SongsService,
// useValue: mockSongsService,
// },
{
provide: 'CONNECTION',
useValue: connection,
},
],
})
export class SongsModule {}
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-04-dependency-injection/lesson-02/src/songs/songs.service.ts
================================================
import { Injectable, Scope } from '@nestjs/common';
@Injectable({
scope: Scope.TRANSIENT,
})
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
// throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-04-dependency-injection/lesson-02/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-04-dependency-injection/lesson-02/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-04-dependency-injection/lesson-02/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "lasting lover",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2022-09-29",
"duration" :"02:34"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [],
synchronize: true,
}),
SongsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(private dataSource: DataSource) {
console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO) {
return this.songsService.create(createSongDTO);
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
// throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "lasting lover",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2022-09-29",
"duration" :"02:34"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/1
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
// import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song],
synchronize: true,
}),
SongsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/song.entity.ts
================================================
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column('varchar', { array: true })
artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO) {
return this.songsService.create(createSongDTO);
}
@Get()
findAll() {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
) {
return `fetch song on the based on id ${typeof id}`;
}
@Put(':id')
update() {
return 'update song on the based on id';
}
@Delete(':id')
delete() {
return 'delete song on the based on id';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
@Module({
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class SongsService {
// local db
// local array
private readonly songs = [];
create(song) {
// Save the song in the database
this.songs.push(song);
return this.songs;
}
findAll() {
// fetch the songs from the db
// Errors comes while fetching the data from DB
// throw new Error('Error in Db whil fetching record');
return this.songs;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-02/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "lasting lover 2",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2022-09-29",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
// import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song],
synchronize: true,
}),
SongsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/song.entity.ts
================================================
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column('varchar', { array: true })
artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(): Promise {
try {
return this.songsService.findAll();
} catch (e) {
throw new HttpException(
'server error',
HttpStatus.INTERNAL_SERVER_ERROR,
{
cause: e,
},
);
}
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
) {}
create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-03/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "New Song 5",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
// import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song],
synchronize: true,
}),
SongsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/song.entity.ts
================================================
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column('varchar', { array: true })
artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
) {}
create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-05-connect-nestjs-to-postgress/lesson-04/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-06-relations/lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-06-relations/lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-06-relations/lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-06-relations/lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-06-relations/lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-06-relations/lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-06-relations/lesson-01/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "New Song 5",
"artists": [
"Siagla",
"Martin",
"John"
],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
================================================
FILE: module-06-relations/lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-06-relations/lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-06-relations/lesson-01/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
// import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User],
synchronize: true,
}),
SongsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-06-relations/lesson-01/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-06-relations/lesson-01/src/artists/artist.entity.ts
================================================
import { User } from 'src/users/user.entity';
import { Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
}
================================================
FILE: module-06-relations/lesson-01/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-06-relations/lesson-01/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-06-relations/lesson-01/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-06-relations/lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-06-relations/lesson-01/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-06-relations/lesson-01/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsString({ each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-06-relations/lesson-01/src/songs/song.entity.ts
================================================
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
@Column('varchar', { array: true })
artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
}
================================================
FILE: module-06-relations/lesson-01/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-06-relations/lesson-01/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-06-relations/lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-06-relations/lesson-01/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-06-relations/lesson-01/src/songs/songs.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
) {}
create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-06-relations/lesson-01/src/users/user.entity.ts
================================================
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
email: string;
@Column()
password: string;
}
================================================
FILE: module-06-relations/lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-06-relations/lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-06-relations/lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-06-relations/lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "You for me 3",
"artists": [1,2],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/src/users/user.entity.ts
================================================
import { Playlist } from 'src/playlists/playlist.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column()
email: string;
@Column()
password: string;
/**
* A user can create many playLists
*/
@OneToMany(() => Playlist, (playList) => playList.user)
playLists: Playlist[];
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-06-relations/lesson-02-and-lesson-03/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-07-authetication-and-authorization/lesson-01/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-07-authetication-and-authorization/lesson-01/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-07-authetication-and-authorization/lesson-01/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3",
"bcryptjs": "^2.4.3"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4",
"@types/bcryptjs": "^2.4.2"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "You for me 3",
"artists": [1,2],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
### Signup User
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "john",
"lastName": "doe",
"email": "john12@gmail.com",
"password": "123456"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone-01',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/auth/auth.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDTO } from 'src/users/dto/create-user.dto';
import { User } from 'src/users/user.entity';
import { UsersService } from 'src/users/users.service';
@Controller('auth')
export class AuthController {
constructor(private userService: UsersService) {}
@Post('signup')
signup(
@Body()
userDTO: CreateUserDTO,
): Promise {
return this.userService.create(userDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
@Module({
imports: [UsersModule],
providers: [AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/auth/auth.service.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class AuthService {}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/users/dto/create-user.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDTO {
@IsString()
@IsNotEmpty()
firstName: string;
@IsString()
@IsNotEmpty()
lastName: string;
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/users/user.entity.ts
================================================
import { Exclude } from 'class-transformer';
import { Playlist } from 'src/playlists/playlist.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column()
@Exclude()
password: string;
/**
* A user can create many playLists
*/
@OneToMany(() => Playlist, (playList) => playList.user)
playLists: Playlist[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/users/users.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/users/users.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/src/users/users.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDTO } from './dto/create-user.dto';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository, // 1.
) {}
async create(userDTO: CreateUserDTO): Promise {
const salt = await bcrypt.genSalt(); // 2.
userDTO.password = await bcrypt.hash(userDTO.password, salt); // 3.
const user = await this.userRepository.save(userDTO); // 4.
delete user.password; // 5.
return user; // 6.
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-01/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-07-authetication-and-authorization/lesson-01/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-07-authetication-and-authorization/lesson-02/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-07-authetication-and-authorization/lesson-02/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-07-authetication-and-authorization/lesson-02/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3",
"bcryptjs": "^2.4.3",
"@nestjs/passport": "^9.0.3",
"passport": "^0.6.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4",
"@types/bcryptjs": "^2.4.2"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "You for me 3",
"artists": [1,2],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
### Signup User
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "john",
"lastName": "doe",
"email": "john12@gmail.com",
"password": "123456"
}
### Login User
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "john12@gmail.com",
"password": "123456"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/app.controller.ts
================================================
import { Controller, Get } from '@nestjs/common';
import { AppService } from './app.service';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone-01',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/auth.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDTO } from 'src/users/dto/create-user.dto';
import { User } from 'src/users/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthService } from './auth.service';
import { LoginDTO } from './dto/login.dto';
@Controller('auth')
export class AuthController {
constructor(
private userService: UsersService,
private authService: AuthService,
) {}
@Post('signup')
signup(
@Body()
userDTO: CreateUserDTO,
): Promise {
return this.userService.create(userDTO);
}
@Post('login')
login(
@Body()
loginDTO: LoginDTO,
) {
return this.authService.login(loginDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
@Module({
imports: [UsersModule],
providers: [AuthService],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/auth.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import { LoginDTO } from './dto/login.dto';
import { User } from 'src/users/user.entity';
import * as bcrypt from 'bcryptjs';
@Injectable()
export class AuthService {
constructor(private userService: UsersService) {}
async login(loginDTO: LoginDTO): Promise {
const user = await this.userService.findOne(loginDTO); // 1.
const passwordMatched = await bcrypt.compare(
loginDTO.password,
user.password,
); // 2.
if (passwordMatched) {
//3
delete user.password; // 4.
return user;
} else {
throw new UnauthorizedException('Password does not match'); // 5.
}
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/auth/dto/login.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDTO {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/users/dto/create-user.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDTO {
@IsString()
@IsNotEmpty()
firstName: string;
@IsString()
@IsNotEmpty()
lastName: string;
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/users/user.entity.ts
================================================
import { Exclude } from 'class-transformer';
import { Playlist } from 'src/playlists/playlist.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column()
@Exclude()
password: string;
/**
* A user can create many playLists
*/
@OneToMany(() => Playlist, (playList) => playList.user)
playLists: Playlist[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/users/users.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/users/users.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/src/users/users.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { User } from './user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDTO } from './dto/create-user.dto';
import * as bcrypt from 'bcryptjs';
import { LoginDTO } from 'src/auth/dto/login.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository, // 1.
) {}
async create(userDTO: CreateUserDTO): Promise {
const salt = await bcrypt.genSalt(); // 2.
userDTO.password = await bcrypt.hash(userDTO.password, salt); // 3.
const user = await this.userRepository.save(userDTO); // 4.
delete user.password; // 5.
return user; // 6.
}
async findOne(data: LoginDTO): Promise {
const user = await this.userRepository.findOneBy({ email: data.email });
if (!user) {
throw new UnauthorizedException('Could not find user');
}
return user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-02/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-07-authetication-and-authorization/lesson-02/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-07-authetication-and-authorization/lesson-03/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-07-authetication-and-authorization/lesson-03/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-07-authetication-and-authorization/lesson-03/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3",
"bcryptjs": "^2.4.3",
"@nestjs/passport": "^9.0.3",
"passport": "^0.6.0",
"@nestjs/jwt": "^10.0.3",
"passport-jwt": "^4.0.1"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4",
"@types/bcryptjs": "^2.4.2",
"@types/passport-jwt": "^3.0.8"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
{
"title": "You for me 3",
"artists": [1,2],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
### Signup User
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "john",
"lastName": "doe",
"email": "john12@gmail.com",
"password": "123456"
}
### Login User
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "john12@gmail.com",
"password": "123456"
}
## Access TOKEN : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJzdWIiOjEsImlhdCI6MTY4NDg1NTYyMSwiZXhwIjoxNjg0OTQyMDIxfQ.4FAABSVzS_6NUAjldhn7-EZ0UbAUUfKgGZ0Qv4tma7M
### Profile
GET http://localhost:3000/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJzdWIiOjEsImlhdCI6MTY4NDg1NTYyMSwiZXhwIjoxNjg0OTQyMDIxfQ.4FAABSVzS_6NUAjldhn7-EZ0UbAUUfKgGZ0Qv4tma7M
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/app.controller.ts
================================================
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { JwtAuthGaurd } from './auth/jwt-guard';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('profile')
@UseGuards(JwtAuthGaurd)
getProfile(
@Req()
request,
) {
return request.user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone-01',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
AuthModule,
UsersModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.constants.ts
================================================
export const authConstants = {
secret: 'HAD_12X#@',
};
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDTO } from 'src/users/dto/create-user.dto';
import { User } from 'src/users/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthService } from './auth.service';
import { LoginDTO } from './dto/login.dto';
@Controller('auth')
export class AuthController {
constructor(
private userService: UsersService,
private authService: AuthService,
) {}
@Post('signup')
signup(
@Body()
userDTO: CreateUserDTO,
): Promise {
return this.userService.create(userDTO);
}
@Post('login')
login(
@Body()
loginDTO: LoginDTO,
) {
return this.authService.login(loginDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { authConstants } from './auth.constants';
import { JwtStrategy } from './jwt-strategy';
@Module({
imports: [
UsersModule,
JwtModule.register({
secret: authConstants.secret,
signOptions: {
expiresIn: '1d',
},
}),
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/auth.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import { LoginDTO } from './dto/login.dto';
import { User } from 'src/users/user.entity';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
@Injectable()
export class AuthService {
constructor(
private userService: UsersService,
private jwtService: JwtService,
) {}
async login(loginDTO: LoginDTO): Promise<{ accessToken: string }> {
const user = await this.userService.findOne(loginDTO); // 1.
const passwordMatched = await bcrypt.compare(
loginDTO.password,
user.password,
);
if (passwordMatched) {
delete user.password;
const payload = { email: user.email, sub: user.id };
return {
accessToken: this.jwtService.sign(payload),
};
} else {
throw new UnauthorizedException('Password does not match'); // 5.
}
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/dto/login.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDTO {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/jwt-guard.ts
================================================
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGaurd extends AuthGuard('jwt') {}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/auth/jwt-strategy.ts
================================================
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { authConstants } from './auth.constants';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: authConstants.secret,
});
}
async validate(payload: any) {
return { userId: payload.sub, email: payload.email };
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
create(@Body() createSongDTO: CreateSongDTO): Promise {
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/users/dto/create-user.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDTO {
@IsString()
@IsNotEmpty()
firstName: string;
@IsString()
@IsNotEmpty()
lastName: string;
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/users/user.entity.ts
================================================
import { Exclude } from 'class-transformer';
import { Playlist } from 'src/playlists/playlist.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column()
@Exclude()
password: string;
/**
* A user can create many playLists
*/
@OneToMany(() => Playlist, (playList) => playList.user)
playLists: Playlist[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/users/users.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/users/users.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/src/users/users.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { User } from './user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDTO } from './dto/create-user.dto';
import * as bcrypt from 'bcryptjs';
import { LoginDTO } from 'src/auth/dto/login.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository, // 1.
) {}
async create(userDTO: CreateUserDTO): Promise {
const salt = await bcrypt.genSalt(); // 2.
userDTO.password = await bcrypt.hash(userDTO.password, salt); // 3.
const user = await this.userRepository.save(userDTO); // 4.
delete user.password; // 5.
return user; // 6.
}
async findOne(data: LoginDTO): Promise {
const user = await this.userRepository.findOneBy({ email: data.email });
if (!user) {
throw new UnauthorizedException('Could not find user');
}
return user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-03/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-07-authetication-and-authorization/lesson-03/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-07-authetication-and-authorization/lesson-04/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-07-authetication-and-authorization/lesson-04/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-07-authetication-and-authorization/lesson-04/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3",
"bcryptjs": "^2.4.3",
"@nestjs/passport": "^9.0.3",
"passport": "^0.6.0",
"@nestjs/jwt": "^10.0.3",
"passport-jwt": "^4.0.1"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4",
"@types/bcryptjs": "^2.4.2",
"@types/passport-jwt": "^3.0.8"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkxMTMsImV4cCI6MTY4NDk5NTUxM30.u7vwcccTXkbMIZvg1k0ZOA_dD1TvzZRDbO6xm8w23Bc
{
"title": "Love again",
"artists": [1],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
### Signup User
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "john",
"lastName": "doe",
"email": "john12@gmail.com",
"password": "123456"
}
### Signup Artist
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "Martin",
"lastName": "Garrix",
"email": "martingarrix@gmail.com",
"password": "123456"
}
### Login Artist
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "martingarrix@gmail.com",
"password": "123456"
}
### Artist Token Temp: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkxMTMsImV4cCI6MTY4NDk5NTUxM30.u7vwcccTXkbMIZvg1k0ZOA_dD1TvzZRDbO6xm8w23Bc
### Login User
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "john12@gmail.com",
"password": "123456"
}
## Access TOKEN : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJzdWIiOjEsImlhdCI6MTY4NDg1NTYyMSwiZXhwIjoxNjg0OTQyMDIxfQ.4FAABSVzS_6NUAjldhn7-EZ0UbAUUfKgGZ0Qv4tma7M
### Profile
GET http://localhost:3000/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkwNzIsImV4cCI6MTY4NDk5NTQ3Mn0.wYEhyDMor-bs2_Ghmcno0mEJqkqkP9XwOrKUDf0YAZc
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/app.controller.ts
================================================
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { JwtAuthGaurd } from './auth/jwt-guard';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('profile')
@UseGuards(JwtAuthGaurd)
getProfile(
@Req()
request,
) {
return request.user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ArtistsModule } from './artists/artists.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone-01',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
AuthModule,
UsersModule,
ArtistsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artists.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ArtistsController } from './artists.controller';
describe('ArtistsController', () => {
let controller: ArtistsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ArtistsController],
}).compile();
controller = module.get(ArtistsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artists.controller.ts
================================================
import { Controller } from '@nestjs/common';
@Controller('artists')
export class ArtistsController {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artists.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Artist } from './artist.entity';
import { ArtistsService } from './artists.service';
import { ArtistsController } from './artists.controller';
@Module({
imports: [TypeOrmModule.forFeature([Artist])],
providers: [ArtistsService],
controllers: [ArtistsController],
exports: [ArtistsService],
})
export class ArtistsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artists.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ArtistsService } from './artists.service';
describe('ArtistsService', () => {
let service: ArtistsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ArtistsService],
}).compile();
service = module.get(ArtistsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/artists/artists.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Artist } from './artist.entity';
@Injectable()
export class ArtistsService {
constructor(
@InjectRepository(Artist)
private artistRepo: Repository,
) {}
findArtist(userId: number): Promise {
return this.artistRepo.findOneBy({ user: { id: userId } });
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/artists-jwt-guard.ts
================================================
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
@Injectable()
export class ArtistJwtGuard extends AuthGuard('jwt') {
canActivate(
context: ExecutionContext,
): boolean | Promise | Observable {
return super.canActivate(context);
}
handleRequest(err: any, user: any): TUser {
if (err || !user) {
throw err || new UnauthorizedException();
}
console.log(user);
if (user.artistId) {
return user;
}
throw err || new UnauthorizedException();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.constants.ts
================================================
export const authConstants = {
secret: 'HAD_12X#@',
};
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { CreateUserDTO } from 'src/users/dto/create-user.dto';
import { User } from 'src/users/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthService } from './auth.service';
import { LoginDTO } from './dto/login.dto';
@Controller('auth')
export class AuthController {
constructor(
private userService: UsersService,
private authService: AuthService,
) {}
@Post('signup')
signup(
@Body()
userDTO: CreateUserDTO,
): Promise {
return this.userService.create(userDTO);
}
@Post('login')
login(
@Body()
loginDTO: LoginDTO,
) {
return this.authService.login(loginDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { authConstants } from './auth.constants';
import { JwtStrategy } from './jwt-strategy';
import { ArtistsModule } from 'src/artists/artists.module';
@Module({
imports: [
UsersModule,
JwtModule.register({
secret: authConstants.secret,
signOptions: {
expiresIn: '1d',
},
}),
ArtistsModule,
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/auth.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import { LoginDTO } from './dto/login.dto';
import { User } from 'src/users/user.entity';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
import { ArtistsService } from 'src/artists/artists.service';
import { PayloadType } from './types';
@Injectable()
export class AuthService {
constructor(
private userService: UsersService,
private jwtService: JwtService,
private artistsService: ArtistsService,
) {}
async login(loginDTO: LoginDTO): Promise<{ accessToken: string }> {
const user = await this.userService.findOne(loginDTO); // 1.
const passwordMatched = await bcrypt.compare(
loginDTO.password,
user.password,
);
if (passwordMatched) {
delete user.password;
const payload: PayloadType = { email: user.email, userId: user.id };
const artist = await this.artistsService.findArtist(user.id); // 2
if (artist) {
payload.artistId = artist.id;
}
return {
accessToken: this.jwtService.sign(payload),
};
} else {
throw new UnauthorizedException('Password does not match'); // 5.
}
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/dto/login.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDTO {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/jwt-guard.ts
================================================
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGaurd extends AuthGuard('jwt') {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/jwt-strategy.ts
================================================
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { authConstants } from './auth.constants';
import { PayloadType } from './types';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: authConstants.secret,
});
}
async validate(payload: PayloadType) {
return {
userId: payload.userId,
email: payload.email,
artistId: payload.artistId,
};
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/auth/types.ts
================================================
export interface PayloadType {
email: string;
userId: number;
artistId?: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
UseGuards,
Request,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
import { ArtistJwtGuard } from 'src/auth/artists-jwt-guard';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
@UseGuards(ArtistJwtGuard)
create(
@Body() createSongDTO: CreateSongDTO,
@Request()
request,
): Promise {
console.log('request.user: ', request.user);
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise {
return this.songsRepository.delete(id);
}
update(id: number, recordToUpdate: UpdateSongDto): Promise {
return this.songsRepository.update(id, recordToUpdate);
}
async paginate(options: IPaginationOptions): Promise> {
const queryBuilder = this.songsRepository.createQueryBuilder('c');
queryBuilder.orderBy('c.releasedDate', 'DESC');
return paginate(queryBuilder, options);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/users/dto/create-user.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class CreateUserDTO {
@IsString()
@IsNotEmpty()
firstName: string;
@IsString()
@IsNotEmpty()
lastName: string;
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/users/user.entity.ts
================================================
import { Exclude } from 'class-transformer';
import { Playlist } from 'src/playlists/playlist.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('users')
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
firstName: string;
@Column()
lastName: string;
@Column({ unique: true })
email: string;
@Column()
@Exclude()
password: string;
/**
* A user can create many playLists
*/
@OneToMany(() => Playlist, (playList) => playList.user)
playLists: Playlist[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/users/users.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Module({
imports: [TypeOrmModule.forFeature([User])],
providers: [UsersService],
exports: [UsersService],
})
export class UsersModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/users/users.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { UsersService } from './users.service';
describe('UsersService', () => {
let service: UsersService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [UsersService],
}).compile();
service = module.get(UsersService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/src/users/users.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { User } from './user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { CreateUserDTO } from './dto/create-user.dto';
import * as bcrypt from 'bcryptjs';
import { LoginDTO } from 'src/auth/dto/login.dto';
@Injectable()
export class UsersService {
constructor(
@InjectRepository(User)
private userRepository: Repository, // 1.
) {}
async create(userDTO: CreateUserDTO): Promise {
const salt = await bcrypt.genSalt(); // 2.
userDTO.password = await bcrypt.hash(userDTO.password, salt); // 3.
const user = await this.userRepository.save(userDTO); // 4.
delete user.password; // 5.
return user; // 6.
}
async findOne(data: LoginDTO): Promise {
const user = await this.userRepository.findOneBy({ email: data.email });
if (!user) {
throw new UnauthorizedException('Could not find user');
}
return user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/test/app.e2e-spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
describe('AppController (e2e)', () => {
let app: INestApplication;
beforeEach(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
it('/ (GET)', () => {
return request(app.getHttpServer())
.get('/')
.expect(200)
.expect('Hello World!');
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-04/test/jest-e2e.json
================================================
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/tsconfig.build.json
================================================
{
"extends": "./tsconfig.json",
"exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
}
================================================
FILE: module-07-authetication-and-authorization/lesson-04/tsconfig.json
================================================
{
"compilerOptions": {
"module": "commonjs",
"declaration": true,
"removeComments": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"target": "es2017",
"sourceMap": true,
"outDir": "./dist",
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,
"noFallthroughCasesInSwitch": false
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/.eslintrc.js
================================================
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
};
================================================
FILE: module-07-authetication-and-authorization/lesson-05/.gitignore
================================================
# compiled output
/dist
/node_modules
# Logs
logs
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
================================================
FILE: module-07-authetication-and-authorization/lesson-05/.prettierrc
================================================
{
"singleQuote": true,
"trailingComma": "all"
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/README.md
================================================
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
A progressive Node.js framework for building efficient and scalable server-side applications.
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Installation
```bash
$ npm install
```
## Running the app
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Test
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](LICENSE).
================================================
FILE: module-07-authetication-and-authorization/lesson-05/nest-cli.json
================================================
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/package.json
================================================
{
"name": "n-fundamentals-pro",
"version": "0.0.1",
"description": "",
"author": "",
"private": true,
"license": "UNLICENSED",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json"
},
"dependencies": {
"@nestjs/common": "^9.0.0",
"@nestjs/core": "^9.0.0",
"@nestjs/platform-express": "^9.0.0",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"@nestjs/typeorm": "^9.0.1",
"pg": "^8.10.0",
"typeorm": "^0.3.15",
"nestjs-typeorm-paginate": "^4.0.3",
"bcryptjs": "^2.4.3",
"@nestjs/passport": "^9.0.3",
"passport": "^0.6.0",
"@nestjs/jwt": "^10.0.3",
"passport-jwt": "^4.0.1",
"speakeasy": "^2.0.0"
},
"devDependencies": {
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/jest": "29.2.4",
"@types/node": "18.11.18",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
"eslint": "^8.0.1",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "29.3.1",
"prettier": "^2.3.2",
"source-map-support": "^0.5.20",
"supertest": "^6.1.3",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4",
"@types/bcryptjs": "^2.4.2",
"@types/passport-jwt": "^3.0.8",
"@types/speakeasy": "^2.0.7"
},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/rest-client.http
================================================
GET http://localhost:3000
### SEND FETCH SONGS REQUEST
GET http://localhost:3000/songs/?page=1&limit=2
### Find SONGS REQUEST
GET http://localhost:3000/songs/1
### Create New SONGS REQUEST
POST http://localhost:3000/songs
Content-Type: application/json
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkxMTMsImV4cCI6MTY4NDk5NTUxM30.u7vwcccTXkbMIZvg1k0ZOA_dD1TvzZRDbO6xm8w23Bc
{
"title": "Love again",
"artists": [1],
"releasedDate" : "2023-05-11",
"duration" :"02:34",
"lyrics": "Sby, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
PUT http://localhost:3000/songs/2
Content-Type: application/json
{
"title": "Animals",
"artists": [
"Martin"
],
"releasedDate" : "2023-02-02",
"duration" :"03:43",
"lyrics": "ANIM, you're my adrenaline. Brought out this other side of me You don't even know Controlling my whole anatomy, oh Fingers are holding you right at the edge You're slipping out of my hands Keeping my secrets all up in my head I'm scared that you won't want me back, oh I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya I wish that I was honest when I had you I shoulda told you that I wanted you for me I dance to every song like it's about ya I drink 'til I kiss someone who looks like ya"
}
### Update SONGS REQUEST
DELETE http://localhost:3000/songs/1
### Create new PlayList
POST http://localhost:3000/playlists
Content-Type: application/json
{
"name": "Feel Good Now",
"songs": [
6
],
"user": 2
}
### Signup User
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "john",
"lastName": "doe",
"email": "john12@gmail.com",
"password": "123456"
}
### Signup Artist
POST http://localhost:3000/auth/signup
Content-Type: application/json
{
"firstName": "Martin",
"lastName": "Garrix",
"email": "martingarrix@gmail.com",
"password": "123456"
}
### Login Artist
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "martingarrix@gmail.com",
"password": "123456"
}
### Artist Token Temp: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkxMTMsImV4cCI6MTY4NDk5NTUxM30.u7vwcccTXkbMIZvg1k0ZOA_dD1TvzZRDbO6xm8w23Bc
### Login User
POST http://localhost:3000/auth/login
Content-Type: application/json
{
"email": "john12@gmail.com",
"password": "123456"
}
## Access TOKEN : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJzdWIiOjEsImlhdCI6MTY4NDg1NTYyMSwiZXhwIjoxNjg0OTQyMDIxfQ.4FAABSVzS_6NUAjldhn7-EZ0UbAUUfKgGZ0Qv4tma7M
### Profile
GET http://localhost:3000/profile
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6Im1hcnRpbmdhcnJpeEBnbWFpbC5jb20iLCJ1c2VySWQiOjIsImFydGlzdElkIjoxLCJpYXQiOjE2ODQ5MDkwNzIsImV4cCI6MTY4NDk5NTQ3Mn0.wYEhyDMor-bs2_Ghmcno0mEJqkqkP9XwOrKUDf0YAZc
### Enable 2FA
GET http://localhost:3000/auth/enable-2fa
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJ1c2VySWQiOjEsImlhdCI6MTY4NDkxMTk3OCwiZXhwIjoxNjg0OTk4Mzc4fQ.qbBHZfu0VL_tY_bC2ccl1I_Xoc0IqG6wAk-D2-tZDa8
### Validate 2FA Token
POST http://localhost:3000/auth/validate-2fa
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJ1c2VySWQiOjEsImlhdCI6MTY4NDkxMTk3OCwiZXhwIjoxNjg0OTk4Mzc4fQ.qbBHZfu0VL_tY_bC2ccl1I_Xoc0IqG6wAk-D2-tZDa8
Content-Type: application/json
{
"token": "993913"
}
### Disable 2FA
GET http://localhost:3000/auth/disable-2fa
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImpvaG4xMkBnbWFpbC5jb20iLCJ1c2VySWQiOjEsImlhdCI6MTY4NDkxMTk3OCwiZXhwIjoxNjg0OTk4Mzc4fQ.qbBHZfu0VL_tY_bC2ccl1I_Xoc0IqG6wAk-D2-tZDa8
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/app.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';
describe('AppController', () => {
let appController: AppController;
beforeEach(async () => {
const app: TestingModule = await Test.createTestingModule({
controllers: [AppController],
providers: [AppService],
}).compile();
appController = app.get(AppController);
});
describe('root', () => {
it('should return "Hello World!"', () => {
expect(appController.getHello()).toBe('Hello World!');
});
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/app.controller.ts
================================================
import { Controller, Get, Req, UseGuards } from '@nestjs/common';
import { AppService } from './app.service';
import { JwtAuthGuard } from './auth/jwt-guard';
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
@Get('profile')
@UseGuards(JwtAuthGuard)
getProfile(
@Req()
request,
) {
return request.user;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/app.module.ts
================================================
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LoggerMiddleware } from './common/middleware/logger.middleware';
import { SongsController } from './songs/songs.controller';
import { SongsModule } from './songs/songs.module';
import { Song } from './songs/song.entity';
import { Artist } from './artists/artist.entity';
import { User } from './users/user.entity';
import { Playlist } from './playlists/playlist.entity';
import { PlayListModule } from './playlists/playlists.module';
// import { DataSource } from 'typeorm';
import { AuthModule } from './auth/auth.module';
import { UsersModule } from './users/users.module';
import { ArtistsModule } from './artists/artists.module';
@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
database: 'spotify-clone-01',
host: 'localhost',
port: 5432,
username: 'postgres',
password: 'root',
entities: [Song, Artist, User, Playlist],
synchronize: true,
}),
SongsModule,
PlayListModule,
AuthModule,
UsersModule,
ArtistsModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
constructor(/*private dataSource: DataSource*/) {
// console.log('dbName ', dataSource.driver.database);
}
configure(consumer: MiddlewareConsumer) {
// consumer.apply(LoggerMiddleware).forRoutes('songs'); // option no 1
// consumer
// .apply(LoggerMiddleware)
// .forRoutes({ path: 'songs', method: RequestMethod.POST }); //option no 2
consumer.apply(LoggerMiddleware).forRoutes(SongsController); //option no 3
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/app.service.ts
================================================
import { Inject, Injectable } from '@nestjs/common';
import { DevConfigService } from './common/providers/DevConfigService';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello I am learning Nest.js Fundamentals';
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Entity,
JoinColumn,
ManyToMany,
OneToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('artists')
export class Artist {
@PrimaryGeneratedColumn()
id: number;
@OneToOne(() => User)
@JoinColumn()
user: User;
@ManyToMany(() => Song, (song) => song.artists)
songs: Song[];
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artists.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ArtistsController } from './artists.controller';
describe('ArtistsController', () => {
let controller: ArtistsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ArtistsController],
}).compile();
controller = module.get(ArtistsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artists.controller.ts
================================================
import { Controller } from '@nestjs/common';
@Controller('artists')
export class ArtistsController {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artists.module.ts
================================================
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Artist } from './artist.entity';
import { ArtistsService } from './artists.service';
import { ArtistsController } from './artists.controller';
@Module({
imports: [TypeOrmModule.forFeature([Artist])],
providers: [ArtistsService],
controllers: [ArtistsController],
exports: [ArtistsService],
})
export class ArtistsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artists.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { ArtistsService } from './artists.service';
describe('ArtistsService', () => {
let service: ArtistsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [ArtistsService],
}).compile();
service = module.get(ArtistsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/artists/artists.service.ts
================================================
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Artist } from './artist.entity';
@Injectable()
export class ArtistsService {
constructor(
@InjectRepository(Artist)
private artistRepo: Repository,
) {}
findArtist(userId: number): Promise {
return this.artistRepo.findOneBy({ user: { id: userId } });
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/artists-jwt-guard.ts
================================================
import {
ExecutionContext,
Injectable,
UnauthorizedException,
} from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { Observable } from 'rxjs';
@Injectable()
export class ArtistJwtGuard extends AuthGuard('jwt') {
canActivate(
context: ExecutionContext,
): boolean | Promise | Observable {
return super.canActivate(context);
}
handleRequest(err: any, user: any): TUser {
if (err || !user) {
throw err || new UnauthorizedException();
}
console.log(user);
if (user.artistId) {
return user;
}
throw err || new UnauthorizedException();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.constants.ts
================================================
export const authConstants = {
secret: 'HAD_12X#@',
};
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthController } from './auth.controller';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AuthController],
}).compile();
controller = module.get(AuthController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.controller.ts
================================================
import {
Body,
Controller,
Get,
Post,
Request,
UseGuards,
} from '@nestjs/common';
import { CreateUserDTO } from 'src/users/dto/create-user.dto';
import { User } from 'src/users/user.entity';
import { UsersService } from 'src/users/users.service';
import { AuthService } from './auth.service';
import { LoginDTO } from './dto/login.dto';
import { JwtAuthGuard } from './jwt-guard';
import { Enable2FAType } from './types';
import { ValidateTokenDTO } from './dto/validate-token.dto';
import { UpdateResult } from 'typeorm';
@Controller('auth')
export class AuthController {
constructor(
private userService: UsersService,
private authService: AuthService,
) {}
@Post('signup')
signup(
@Body()
userDTO: CreateUserDTO,
): Promise {
return this.userService.create(userDTO);
}
@Post('login')
login(
@Body()
loginDTO: LoginDTO,
) {
return this.authService.login(loginDTO);
}
@Get('enable-2fa')
@UseGuards(JwtAuthGuard)
enable2FA(
@Request()
req,
): Promise {
console.log(req.user);
return this.authService.enable2FA(req.user.userId);
}
@Post('validate-2fa')
@UseGuards(JwtAuthGuard)
validate2FA(
@Request()
req,
@Body()
ValidateTokenDTO: ValidateTokenDTO,
): Promise<{ verified: boolean }> {
return this.authService.validate2FAToken(
req.user.userId,
ValidateTokenDTO.token,
);
}
@Get('disable-2fa')
@UseGuards(JwtAuthGuard)
disable2FA(
@Request()
req,
): Promise {
return this.authService.disable2FA(req.user.userId);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.module.ts
================================================
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from 'src/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { authConstants } from './auth.constants';
import { JwtStrategy } from './jwt-strategy';
import { ArtistsModule } from 'src/artists/artists.module';
@Module({
imports: [
UsersModule,
JwtModule.register({
secret: authConstants.secret,
signOptions: {
expiresIn: '1d',
},
}),
ArtistsModule,
],
providers: [AuthService, JwtStrategy],
controllers: [AuthController],
exports: [AuthService],
})
export class AuthModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { AuthService } from './auth.service';
describe('AuthService', () => {
let service: AuthService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
}).compile();
service = module.get(AuthService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/auth.service.ts
================================================
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { UsersService } from 'src/users/users.service';
import { LoginDTO } from './dto/login.dto';
import { User } from 'src/users/user.entity';
import * as bcrypt from 'bcryptjs';
import { JwtService } from '@nestjs/jwt';
import { ArtistsService } from 'src/artists/artists.service';
import { Enable2FAType, PayloadType } from './types';
import * as speakeasy from 'speakeasy';
import { UpdateResult } from 'typeorm';
@Injectable()
export class AuthService {
constructor(
private userService: UsersService,
private jwtService: JwtService,
private artistsService: ArtistsService,
) {}
async login(
loginDTO: LoginDTO,
): Promise<
{ accessToken: string } | { validate2FA: string; message: string }
> {
const user = await this.userService.findOne(loginDTO); // 1.
const passwordMatched = await bcrypt.compare(
loginDTO.password,
user.password,
);
if (passwordMatched) {
delete user.password;
const payload: PayloadType = { email: user.email, userId: user.id };
const artist = await this.artistsService.findArtist(user.id); // 2
if (artist) {
payload.artistId = artist.id;
}
if (user.enable2FA && user.twoFASecret) {
//1.
// sends the validateToken request link
// else otherwise sends the json web token in the response
return {
//2.
validate2FA: 'http://localhost:3000/auth/validate-2fa',
message:
'Please sends the one time password/token from your Google Authenticator App',
};
}
return {
accessToken: this.jwtService.sign(payload),
};
} else {
throw new UnauthorizedException('Password does not match'); // 5.
}
}
async enable2FA(userId: number): Promise {
const user = await this.userService.findById(userId); //1
if (user.enable2FA) {
//2
return { secret: user.twoFASecret };
}
const secret = speakeasy.generateSecret(); //3
console.log(secret);
user.twoFASecret = secret.base32; //4
await this.userService.updateSecretKey(user.id, user.twoFASecret); //5
return { secret: user.twoFASecret }; //6
}
async validate2FAToken(
userId: number,
token: string,
): Promise<{ verified: boolean }> {
try {
// find the user on the based on id
const user = await this.userService.findById(userId);
// extract his 2FA secret
// verify the secret with token by calling the speakeasy verify method
const verified = speakeasy.totp.verify({
secret: user.twoFASecret,
token: token,
encoding: 'base32',
});
// if validated then sends the json web token in the response
if (verified) {
return { verified: true };
} else {
return { verified: false };
}
} catch (err) {
throw new UnauthorizedException('Error verifying token');
}
}
async disable2FA(userId: number): Promise {
return this.userService.disable2FA(userId);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/dto/login.dto.ts
================================================
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
export class LoginDTO {
@IsEmail()
@IsNotEmpty()
email: string;
@IsString()
@IsNotEmpty()
password: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/dto/validate-token.dto.ts
================================================
import { IsNotEmpty, IsString } from 'class-validator';
export class ValidateTokenDTO {
@IsNotEmpty()
@IsString()
token: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/jwt-guard.ts
================================================
import { Injectable } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/jwt-strategy.ts
================================================
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { authConstants } from './auth.constants';
import { PayloadType } from './types';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: authConstants.secret,
});
}
async validate(payload: PayloadType) {
return {
userId: payload.userId,
email: payload.email,
artistId: payload.artistId,
};
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/auth/types.ts
================================================
export interface PayloadType {
email: string;
userId: number;
artistId?: number;
}
export type Enable2FAType = {
secret: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/common/constatnts/connection.ts
================================================
export const connection: Connection = {
CONNECTION_STRING: 'MYSQL://12324/sad',
DB: 'MYSQL',
DBNAME: 'TEST',
};
export type Connection = {
CONNECTION_STRING: string;
DB: string;
DBNAME: string;
};
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/common/middleware/logger.middleware.ts
================================================
import { Injectable, NestMiddleware } from '@nestjs/common';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
console.log('Request ....', new Date().toDateString());
next();
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/common/providers/DevConfigService.ts
================================================
import { Injectable } from '@nestjs/common';
@Injectable()
export class DevConfigService {
DBHOST = 'localhost';
getDBHOST() {
return this.DBHOST;
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/main.ts
================================================
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/playlists/dto/create-playlist.dto.ts
================================================
import { IsArray, IsNotEmpty, IsNumber, IsString } from 'class-validator';
export class CreatePlayListDto {
@IsString()
@IsNotEmpty()
readonly name;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly songs;
@IsNumber()
@IsNotEmpty()
readonly user: number;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/playlists/playlist.entity.ts
================================================
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
import {
Column,
Entity,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('playlists')
export class Playlist {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
/**
* Each Playlist will have multiple songs
*/
@OneToMany(() => Song, (song) => song.playList)
songs: Song[];
/**
* Many Playlist can belong to a single unique user
*/
@ManyToOne(() => User, (user) => user.playLists)
user: User;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/playlists/playlists.controller.ts
================================================
import { Body, Controller, Post } from '@nestjs/common';
import { Playlist } from './playlist.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
import { PlayListsService } from './playlists.service';
@Controller('playlists')
export class PlayListsController {
constructor(private playListService: PlayListsService) {}
@Post()
create(
@Body()
playlistDTO: CreatePlayListDto,
): Promise {
return this.playListService.create(playlistDTO);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/playlists/playlists.module.ts
================================================
import { Module } from '@nestjs/common';
import { PlayListsController } from './playlists.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { PlayListsService } from './playlists.service';
import { Song } from 'src/songs/song.entity';
import { User } from 'src/users/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([Playlist, Song, User])],
controllers: [PlayListsController],
providers: [PlayListsService],
})
export class PlayListModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/playlists/playlists.service.ts
================================================
import { InjectRepository } from '@nestjs/typeorm';
import { Playlist } from './playlist.entity';
import { Song } from 'src/songs/song.entity';
import { Injectable } from '@nestjs/common';
import { Repository } from 'typeorm';
import { User } from 'src/users/user.entity';
import { CreatePlayListDto } from './dto/create-playlist.dto';
@Injectable()
export class PlayListsService {
constructor(
@InjectRepository(Playlist)
private playListRepo: Repository,
@InjectRepository(Song)
private songsRepo: Repository,
@InjectRepository(User)
private userRepo: Repository,
) {}
async create(playListDTO: CreatePlayListDto): Promise {
const playList = new Playlist();
playList.name = playListDTO.name;
// songs will be the array of ids that we are getting from the DTO object
const songs = await this.songsRepo.findByIds(playListDTO.songs);
// set the relation for the songs with playlist entity
playList.songs = songs;
// A user will be the id of the user we are getting from the request
// when we implemented the user authentication this id will become the loggedIn user id
const user = await this.userRepo.findOneBy({ id: playListDTO.user });
playList.user = user;
return this.playListRepo.save(playList);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/dto/create-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class CreateSongDTO {
@IsString()
@IsNotEmpty()
readonly title;
@IsNotEmpty()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsNotEmpty()
@IsDateString()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsNotEmpty()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/dto/update-song-dto.ts
================================================
import {
IsArray,
IsDateString,
IsMilitaryTime,
IsNumber,
IsOptional,
IsString,
} from 'class-validator';
export class UpdateSongDto {
@IsString()
@IsOptional()
readonly title;
@IsOptional()
@IsArray()
@IsNumber({}, { each: true })
readonly artists;
@IsDateString()
@IsOptional()
readonly releasedDate: Date;
@IsMilitaryTime()
@IsOptional()
readonly duration: Date;
@IsString()
@IsOptional()
readonly lyrics: string;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/song.entity.ts
================================================
import { Artist } from 'src/artists/artist.entity';
import { Playlist } from 'src/playlists/playlist.entity';
import {
Column,
Entity,
JoinTable,
ManyToMany,
ManyToOne,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('songs')
export class Song {
@PrimaryGeneratedColumn()
id: number;
@Column()
title: string;
// @Column('varchar', { array: true })
// artists: string[];
@Column('date')
releasedDate: Date;
@Column('time')
duration: Date;
@Column('text')
lyrics: string;
@ManyToMany(() => Artist, (artist) => artist.songs, { cascade: true })
@JoinTable({ name: 'songs_artists' })
artists: Artist[];
/**
* Many songs can belong to playlist for each unique user
*/
@ManyToOne(() => Playlist, (playList) => playList.songs)
playList: Playlist;
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/songs.controller.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsController } from './songs.controller';
describe('SongsController', () => {
let controller: SongsController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [SongsController],
}).compile();
controller = module.get(SongsController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/songs.controller.ts
================================================
import {
Controller,
Get,
Put,
Delete,
Post,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Body,
Inject,
Scope,
Query,
DefaultValuePipe,
UseGuards,
Request,
} from '@nestjs/common';
import { SongsService } from './songs.service';
import { CreateSongDTO } from './dto/create-song-dto';
import { Song } from './song.entity';
import { DeleteResult, UpdateResult } from 'typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Pagination } from 'nestjs-typeorm-paginate';
import { ArtistJwtGuard } from 'src/auth/artists-jwt-guard';
@Controller('songs')
export class SongsController {
constructor(private songsService: SongsService) {}
@Post()
@UseGuards(ArtistJwtGuard)
create(
@Body() createSongDTO: CreateSongDTO,
@Request()
request,
): Promise {
console.log('request.user: ', request.user);
return this.songsService.create(createSongDTO);
}
@Get()
findAll(
@Query('page', new DefaultValuePipe(1), ParseIntPipe)
page = 1,
@Query('limit', new DefaultValuePipe(10), ParseIntPipe)
limit = 10,
): Promise> {
limit = limit > 100 ? 100 : limit;
return this.songsService.paginate({
page,
limit,
});
}
@Get(':id')
findOne(
@Param(
'id',
new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }),
)
id: number,
): Promise {
return this.songsService.findOne(id);
}
@Put(':id')
update(
@Param('id', ParseIntPipe) id: number,
@Body() updateSongDTO: UpdateSongDto,
): Promise {
return this.songsService.update(id, updateSongDTO);
}
@Delete(':id')
delete(@Param('id', ParseIntPipe) id: number): Promise {
return this.songsService.remove(id);
}
}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/songs.module.ts
================================================
import { Module } from '@nestjs/common';
import { SongsController } from './songs.controller';
import { SongsService } from './songs.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Song } from './song.entity';
import { Artist } from 'src/artists/artist.entity';
@Module({
imports: [TypeOrmModule.forFeature([Song, Artist])],
controllers: [SongsController],
providers: [SongsService],
})
export class SongsModule {}
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/songs.service.spec.ts
================================================
import { Test, TestingModule } from '@nestjs/testing';
import { SongsService } from './songs.service';
describe('SongsService', () => {
let service: SongsService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [SongsService],
}).compile();
service = module.get(SongsService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
================================================
FILE: module-07-authetication-and-authorization/lesson-05/src/songs/songs.service.ts
================================================
import { ConsoleLogger, Injectable } from '@nestjs/common';
import { DeleteResult, Repository, UpdateResult } from 'typeorm';
import {
paginate,
Pagination,
IPaginationOptions,
} from 'nestjs-typeorm-paginate';
import { Song } from './song.entity';
import { CreateSongDTO } from './dto/create-song-dto';
import { InjectRepository } from '@nestjs/typeorm';
import { UpdateSongDto } from './dto/update-song-dto';
import { Artist } from 'src/artists/artist.entity';
@Injectable()
export class SongsService {
constructor(
@InjectRepository(Song)
private songsRepository: Repository,
@InjectRepository(Artist)
private artistsRepository: Repository,
) {}
async create(songDTO: CreateSongDTO): Promise {
const song = new Song();
song.title = songDTO.title;
song.artists = songDTO.artists;
song.duration = songDTO.duration;
song.lyrics = songDTO.lyrics;
song.releasedDate = songDTO.releasedDate;
console.log(songDTO.artists);
// find all the artits on the based on ids
const artists = await this.artistsRepository.findByIds(songDTO.artists);
console.log(artists);
//set the relation with artist and songs
song.artists = artists;
return this.songsRepository.save(song);
}
findAll(): Promise {
return this.songsRepository.find();
}
findOne(id: number): Promise {
return this.songsRepository.findOneBy({ id });
}
remove(id: number): Promise