├── .editorconfig ├── .github ├── renovate.json └── workflows │ └── test.yaml ├── .gitignore ├── .npmignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── commitlint.config.js ├── example ├── nest-cli.json ├── package.json ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── cat │ │ ├── cat.model.ts │ │ ├── cats.controller.ts │ │ ├── cats.module.ts │ │ ├── cats.service.spec.ts │ │ └── cats.service.ts │ ├── database │ │ └── test-database.module.ts │ ├── main.ts │ └── user │ │ ├── user.controller.ts │ │ ├── user.model.ts │ │ ├── user.module.ts │ │ └── user.service.ts ├── tsconfig.build.json ├── tsconfig.json ├── tslint.json └── yarn.lock ├── gulpfile.js ├── package-lock.json ├── package.json ├── src ├── __snapshots__ │ ├── typegoose-core.module.spec.ts.snap │ └── typegoose.providers.spec.ts.snap ├── index.ts ├── typegoose-class.interface.ts ├── typegoose-core.module.spec.ts ├── typegoose-core.module.ts ├── typegoose-options.interface.ts ├── typegoose.constants.ts ├── typegoose.decorators.spec.ts ├── typegoose.decorators.ts ├── typegoose.module.spec.ts ├── typegoose.module.ts ├── typegoose.providers.spec.ts ├── typegoose.providers.ts └── typegoose.utils.ts ├── test ├── app.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json ├── tsconfig.json ├── tslint.json ├── website ├── README.md ├── docs │ ├── async-configuration.md │ ├── discriminators.md │ ├── faq.md │ ├── install.md │ ├── multiple-connections.md │ ├── schema-options.md │ ├── testing.md │ └── usage.md ├── docusaurus.config.js ├── package.json ├── sidebars.js ├── src │ ├── css │ │ └── custom.css │ └── pages │ │ ├── index.js │ │ └── styles.module.css ├── static │ └── img │ │ └── favicon.ico └── yarn.lock └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["config:base"], 3 | "assignees": ["@kpfromer"], 4 | "automerge": true, 5 | "major": { 6 | "automerge": false 7 | }, 8 | "semanticCommits": true, 9 | "ignoreDeps": ["nestjs-typegoose"] 10 | } 11 | -------------------------------------------------------------------------------- /.github/workflows/test.yaml: -------------------------------------------------------------------------------- 1 | name: Test and Release 2 | 3 | on: 4 | pull_request: 5 | push: 6 | branches: [master] 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v2 13 | 14 | - name: Setup Node.js 15 | uses: actions/setup-node@master 16 | with: 17 | node-version: 10.x 18 | 19 | - name: Yarn cache directory 20 | id: yarn-cache-dir 21 | run: echo "::set-output name=dir::$(yarn cache dir)" 22 | 23 | - name: Yarn cache 24 | uses: actions/cache@v1 25 | id: yarn-cache 26 | with: 27 | path: ${{ steps.yarn-cache-dir.outputs.dir }} 28 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 29 | restore-keys: | 30 | ${{ runner.os }}-yarn- 31 | 32 | - name: Install dependencies 33 | run: yarn install --pure-lockfile 34 | 35 | - name: Test 36 | run: yarn coverage 37 | env: 38 | COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} 39 | 40 | - name: Lint 41 | run: yarn lint 42 | 43 | - name: Build 44 | run: yarn build 45 | 46 | npm-release: 47 | # Only release on push to master 48 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 49 | runs-on: ubuntu-latest 50 | # Waits for test jobs for each Node.js version to complete 51 | needs: [test] 52 | steps: 53 | - uses: actions/checkout@v2 54 | with: 55 | # needed for semantic release 56 | # see https://github.com/semantic-release/git/issues/196#issuecomment-702839100 57 | token: ${{ secrets.GITHUB_TOKEN }} 58 | 59 | - name: Setup Node.js 60 | uses: actions/setup-node@master 61 | with: 62 | node-version: 10.x 63 | 64 | - name: Yarn cache directory 65 | id: yarn-cache-dir 66 | run: echo "::set-output name=dir::$(yarn cache dir)" 67 | 68 | - name: Yarn cache 69 | uses: actions/cache@v1 70 | id: yarn-cache 71 | with: 72 | path: ${{ steps.yarn-cache-dir.outputs.dir }} 73 | key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} 74 | restore-keys: | 75 | ${{ runner.os }}-yarn- 76 | 77 | - name: Install 78 | run: yarn install --pure-lockfile 79 | 80 | - name: Build 81 | run: yarn build 82 | 83 | - name: Release 84 | run: npx semantic-release 85 | env: 86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 87 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 88 | 89 | docs-release: 90 | if: github.event_name == 'push' && github.ref == 'refs/heads/master' 91 | runs-on: ubuntu-latest 92 | steps: 93 | - uses: actions/checkout@v1 94 | 95 | - name: Setup Node.js 96 | uses: actions/setup-node@v1 97 | with: 98 | node-version: "10.x" 99 | 100 | - uses: webfactory/ssh-agent@v0.5.0 101 | with: 102 | ssh-private-key: ${{ secrets.GH_PAGES_DEPLOY }} 103 | 104 | - name: Release to GitHub Pages 105 | env: 106 | USE_SSH: true 107 | GIT_USER: git 108 | run: | 109 | cd website 110 | 111 | git config --global user.email "kpfromer2@gmail.com" 112 | git config --global user.name "Kyle Pfromer" 113 | 114 | yarn install --frozen-lockfile 115 | 116 | yarn deploy 117 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/linux,macos,windows,visualstudiocode,node,intellij,webstorm 2 | 3 | ### Intellij ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff: 8 | .idea 9 | .idea/**/tasks.xml 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # Mongo Explorer plugin: 25 | .idea/**/mongoSettings.xml 26 | 27 | ## File-based project format: 28 | *.iws 29 | 30 | ## Plugin-specific files: 31 | 32 | # IntelliJ 33 | /out/ 34 | 35 | # mpeltonen/sbt-idea plugin 36 | .idea_modules/ 37 | 38 | # JIRA plugin 39 | atlassian-ide-plugin.xml 40 | 41 | # Crashlytics plugin (for Android Studio and IntelliJ) 42 | com_crashlytics_export_strings.xml 43 | crashlytics.properties 44 | crashlytics-build.properties 45 | fabric.properties 46 | 47 | ### Intellij Patch ### 48 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 49 | 50 | # *.iml 51 | # modules.xml 52 | # .idea/misc.xml 53 | # *.ipr 54 | 55 | ### Linux ### 56 | *~ 57 | 58 | # temporary files which can be created if a process still has a handle open of a deleted file 59 | .fuse_hidden* 60 | 61 | # KDE directory preferences 62 | .directory 63 | 64 | # Linux trash folder which might appear on any partition or disk 65 | .Trash-* 66 | 67 | # .nfs files are created when an open file is removed but is still being accessed 68 | .nfs* 69 | 70 | ### macOS ### 71 | *.DS_Store 72 | .AppleDouble 73 | .LSOverride 74 | 75 | # Icon must end with two \r 76 | Icon 77 | 78 | 79 | # Thumbnails 80 | ._* 81 | 82 | # Files that might appear in the root of a volume 83 | .DocumentRevisions-V100 84 | .fseventsd 85 | .Spotlight-V100 86 | .TemporaryItems 87 | .Trashes 88 | .VolumeIcon.icns 89 | .com.apple.timemachine.donotpresent 90 | 91 | # Directories potentially created on remote AFP share 92 | .AppleDB 93 | .AppleDesktop 94 | Network Trash Folder 95 | Temporary Items 96 | .apdisk 97 | 98 | ### Node ### 99 | # Logs 100 | logs 101 | *.log 102 | npm-debug.log* 103 | yarn-debug.log* 104 | yarn-error.log* 105 | 106 | # Runtime data 107 | pids 108 | *.pid 109 | *.seed 110 | *.pid.lock 111 | 112 | # Directory for instrumented libs generated by jscoverage/JSCover 113 | lib-cov 114 | 115 | # Coverage directory used by tools like istanbul 116 | coverage 117 | 118 | # nyc test coverage 119 | .nyc_output 120 | 121 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 122 | .grunt 123 | 124 | # Bower dependency directory (https://bower.io/) 125 | bower_components 126 | 127 | # node-waf configuration 128 | .lock-wscript 129 | 130 | # Compiled binary addons (http://nodejs.org/api/addons.html) 131 | build/Release 132 | 133 | # Dependency directories 134 | node_modules/ 135 | jspm_packages/ 136 | 137 | # Typescript v1 declaration files 138 | typings/ 139 | 140 | # Optional npm cache directory 141 | .npm 142 | 143 | # Optional eslint cache 144 | .eslintcache 145 | 146 | # Optional REPL history 147 | .node_repl_history 148 | 149 | # Output of 'npm pack' 150 | *.tgz 151 | 152 | # Yarn Integrity file 153 | .yarn-integrity 154 | 155 | # dotenv environment variables file 156 | .env 157 | 158 | 159 | ### VisualStudioCode ### 160 | .vscode/* 161 | !.vscode/settings.json 162 | !.vscode/tasks.json 163 | !.vscode/launch.json 164 | !.vscode/extensions.json 165 | 166 | ### WebStorm ### 167 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 168 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 169 | 170 | # User-specific stuff: 171 | 172 | # Sensitive or high-churn files: 173 | 174 | # Gradle: 175 | 176 | # Mongo Explorer plugin: 177 | 178 | ## File-based project format: 179 | 180 | ## Plugin-specific files: 181 | 182 | # IntelliJ 183 | 184 | # mpeltonen/sbt-idea plugin 185 | 186 | # JIRA plugin 187 | 188 | # Crashlytics plugin (for Android Studio and IntelliJ) 189 | 190 | ### WebStorm Patch ### 191 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 192 | 193 | # *.iml 194 | # modules.xml 195 | # .idea/misc.xml 196 | # *.ipr 197 | 198 | ### Windows ### 199 | # Windows thumbnail cache files 200 | Thumbs.db 201 | ehthumbs.db 202 | ehthumbs_vista.db 203 | 204 | # Folder config file 205 | Desktop.ini 206 | 207 | # Recycle Bin used on file shares 208 | $RECYCLE.BIN/ 209 | 210 | # Windows Installer files 211 | *.cab 212 | *.msi 213 | *.msm 214 | *.msp 215 | 216 | # Windows shortcuts 217 | *.lnk 218 | 219 | # End of https://www.gitignore.io/api/linux,macos,windows,visualstudiocode,node,intellij,webstorm 220 | 221 | dist/ 222 | build/ 223 | .tsbuildinfo 224 | 225 | # example project 226 | example/dist 227 | example/node_modules 228 | 229 | .docusaurus -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/linux,macos,windows,visualstudiocode,node,intellij,webstorm 2 | 3 | ### Intellij ### 4 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 5 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 6 | 7 | # User-specific stuff: 8 | .idea 9 | .idea/**/tasks.xml 10 | 11 | # Sensitive or high-churn files: 12 | .idea/**/dataSources/ 13 | .idea/**/dataSources.ids 14 | .idea/**/dataSources.xml 15 | .idea/**/dataSources.local.xml 16 | .idea/**/sqlDataSources.xml 17 | .idea/**/dynamic.xml 18 | .idea/**/uiDesigner.xml 19 | 20 | # Gradle: 21 | .idea/**/gradle.xml 22 | .idea/**/libraries 23 | 24 | # Mongo Explorer plugin: 25 | .idea/**/mongoSettings.xml 26 | 27 | ## File-based project format: 28 | *.iws 29 | 30 | ## Plugin-specific files: 31 | 32 | # IntelliJ 33 | /out/ 34 | 35 | # mpeltonen/sbt-idea plugin 36 | .idea_modules/ 37 | 38 | # JIRA plugin 39 | atlassian-ide-plugin.xml 40 | 41 | # Crashlytics plugin (for Android Studio and IntelliJ) 42 | com_crashlytics_export_strings.xml 43 | crashlytics.properties 44 | crashlytics-build.properties 45 | fabric.properties 46 | 47 | ### Intellij Patch ### 48 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 49 | 50 | # *.iml 51 | # modules.xml 52 | # .idea/misc.xml 53 | # *.ipr 54 | 55 | ### Linux ### 56 | *~ 57 | 58 | # temporary files which can be created if a process still has a handle open of a deleted file 59 | .fuse_hidden* 60 | 61 | # KDE directory preferences 62 | .directory 63 | 64 | # Linux trash folder which might appear on any partition or disk 65 | .Trash-* 66 | 67 | # .nfs files are created when an open file is removed but is still being accessed 68 | .nfs* 69 | 70 | ### macOS ### 71 | *.DS_Store 72 | .AppleDouble 73 | .LSOverride 74 | 75 | # Icon must end with two \r 76 | Icon 77 | 78 | 79 | # Thumbnails 80 | ._* 81 | 82 | # Files that might appear in the root of a volume 83 | .DocumentRevisions-V100 84 | .fseventsd 85 | .Spotlight-V100 86 | .TemporaryItems 87 | .Trashes 88 | .VolumeIcon.icns 89 | .com.apple.timemachine.donotpresent 90 | 91 | # Directories potentially created on remote AFP share 92 | .AppleDB 93 | .AppleDesktop 94 | Network Trash Folder 95 | Temporary Items 96 | .apdisk 97 | 98 | ### Node ### 99 | # Logs 100 | logs 101 | *.log 102 | npm-debug.log* 103 | yarn-debug.log* 104 | yarn-error.log* 105 | 106 | # Runtime data 107 | pids 108 | *.pid 109 | *.seed 110 | *.pid.lock 111 | 112 | # Directory for instrumented libs generated by jscoverage/JSCover 113 | lib-cov 114 | 115 | # Coverage directory used by tools like istanbul 116 | coverage 117 | 118 | # nyc test coverage 119 | .nyc_output 120 | 121 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 122 | .grunt 123 | 124 | # Bower dependency directory (https://bower.io/) 125 | bower_components 126 | 127 | # node-waf configuration 128 | .lock-wscript 129 | 130 | # Compiled binary addons (http://nodejs.org/api/addons.html) 131 | build/Release 132 | 133 | # Dependency directories 134 | node_modules/ 135 | jspm_packages/ 136 | 137 | # Typescript v1 declaration files 138 | typings/ 139 | 140 | # Optional npm cache directory 141 | .npm 142 | 143 | # Optional eslint cache 144 | .eslintcache 145 | 146 | # Optional REPL history 147 | .node_repl_history 148 | 149 | # Output of 'npm pack' 150 | *.tgz 151 | 152 | # Yarn Integrity file 153 | .yarn-integrity 154 | 155 | # dotenv environment variables file 156 | .env 157 | 158 | 159 | ### VisualStudioCode ### 160 | .vscode/* 161 | !.vscode/settings.json 162 | !.vscode/tasks.json 163 | !.vscode/launch.json 164 | !.vscode/extensions.json 165 | 166 | ### WebStorm ### 167 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 168 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 169 | 170 | # User-specific stuff: 171 | 172 | # Sensitive or high-churn files: 173 | 174 | # Gradle: 175 | 176 | # Mongo Explorer plugin: 177 | 178 | ## File-based project format: 179 | 180 | ## Plugin-specific files: 181 | 182 | # IntelliJ 183 | 184 | # mpeltonen/sbt-idea plugin 185 | 186 | # JIRA plugin 187 | 188 | # Crashlytics plugin (for Android Studio and IntelliJ) 189 | 190 | ### WebStorm Patch ### 191 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 192 | 193 | # *.iml 194 | # modules.xml 195 | # .idea/misc.xml 196 | # *.ipr 197 | 198 | ### Windows ### 199 | # Windows thumbnail cache files 200 | Thumbs.db 201 | ehthumbs.db 202 | ehthumbs_vista.db 203 | 204 | # Folder config file 205 | Desktop.ini 206 | 207 | # Recycle Bin used on file shares 208 | $RECYCLE.BIN/ 209 | 210 | # Windows Installer files 211 | *.cab 212 | *.msi 213 | *.msm 214 | *.msp 215 | 216 | # Windows shortcuts 217 | *.lnk 218 | 219 | # End of https://www.gitignore.io/api/linux,macos,windows,visualstudiocode,node,intellij,webstorm 220 | 221 | src 222 | tsconfig.json 223 | test 224 | .travis.yml 225 | .gitignore 226 | /src/__snapshots__/ 227 | gulpfile.js 228 | tslint.json 229 | example/ 230 | website -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [7.1.38](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.37...v7.1.38) (2020-09-29) 2 | 3 | 4 | ### Bug Fixes 5 | 6 | * test typing ([97cdb73](https://github.com/kpfromer/nestjs-typegoose/commit/97cdb7340d4f9c1acfa691456b88a61f114d5a8a)) 7 | 8 | ## [7.1.37](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.36...v7.1.37) (2020-09-09) 9 | 10 | 11 | ### Bug Fixes 12 | 13 | * update docs website configuration ([ed8fcdf](https://github.com/kpfromer/nestjs-typegoose/commit/ed8fcdf300bc318188f6e46d25bf814f0b2ed1dd)) 14 | 15 | ## [7.1.36](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.35...v7.1.36) (2020-09-04) 16 | 17 | 18 | ### Bug Fixes 19 | 20 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.63 ([5054d09](https://github.com/kpfromer/nestjs-typegoose/commit/5054d09ab3f350d63954de48252e5f19ec90b266)) 21 | 22 | ## [7.1.35](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.34...v7.1.35) (2020-09-03) 23 | 24 | 25 | ### Bug Fixes 26 | 27 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.63 ([c6f710d](https://github.com/kpfromer/nestjs-typegoose/commit/c6f710dc376da9390169a367bba7b974087fbc4d)) 28 | 29 | ## [7.1.34](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.33...v7.1.34) (2020-08-28) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.62 ([0563b24](https://github.com/kpfromer/nestjs-typegoose/commit/0563b24c38aba4bc4b9954f72e2ed9497d5d0623)) 35 | 36 | ## [7.1.33](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.32...v7.1.33) (2020-08-28) 37 | 38 | 39 | ### Bug Fixes 40 | 41 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.62 ([b572d2e](https://github.com/kpfromer/nestjs-typegoose/commit/b572d2e67ee2e4ed0759ef21349d28d6c559b5af)) 42 | 43 | ## [7.1.32](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.31...v7.1.32) (2020-08-01) 44 | 45 | 46 | ### Bug Fixes 47 | 48 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.61 ([44519e7](https://github.com/kpfromer/nestjs-typegoose/commit/44519e7fb408b34d088245330789d6d696ee9997)) 49 | 50 | ## [7.1.31](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.30...v7.1.31) (2020-08-01) 51 | 52 | 53 | ### Bug Fixes 54 | 55 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.61 ([b64f07e](https://github.com/kpfromer/nestjs-typegoose/commit/b64f07eaf9a9c1fcb834a733a2c367c0a80d94f8)) 56 | 57 | ## [7.1.30](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.29...v7.1.30) (2020-07-29) 58 | 59 | 60 | ### Bug Fixes 61 | 62 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.60 ([fb17d40](https://github.com/kpfromer/nestjs-typegoose/commit/fb17d4084cbd9b6eb82948a36b385dfdb2b37cdb)) 63 | 64 | ## [7.1.29](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.28...v7.1.29) (2020-07-29) 65 | 66 | 67 | ### Bug Fixes 68 | 69 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.60 ([bb2e8a8](https://github.com/kpfromer/nestjs-typegoose/commit/bb2e8a8740967dc0fe68b1351129d12776507a7f)) 70 | 71 | ## [7.1.28](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.27...v7.1.28) (2020-06-18) 72 | 73 | 74 | ### Bug Fixes 75 | 76 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.58 ([69d21e0](https://github.com/kpfromer/nestjs-typegoose/commit/69d21e0b5035f7afc5da9bb03c9361ed5c4b5832)) 77 | 78 | ## [7.1.27](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.26...v7.1.27) (2020-06-18) 79 | 80 | 81 | ### Bug Fixes 82 | 83 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.58 ([9f8e05c](https://github.com/kpfromer/nestjs-typegoose/commit/9f8e05c39e8a01090f77176ff795d58467895481)) 84 | 85 | ## [7.1.26](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.25...v7.1.26) (2020-05-28) 86 | 87 | 88 | ### Bug Fixes 89 | 90 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.56 ([0491efb](https://github.com/kpfromer/nestjs-typegoose/commit/0491efb739078b147e712d2fe26e2891a66d7292)) 91 | 92 | ## [7.1.25](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.24...v7.1.25) (2020-05-27) 93 | 94 | 95 | ### Bug Fixes 96 | 97 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.56 ([8a6d4c2](https://github.com/kpfromer/nestjs-typegoose/commit/8a6d4c2b3fb771cfd7ce078d30fbce04b184e76f)) 98 | 99 | ## [7.1.24](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.23...v7.1.24) (2020-05-19) 100 | 101 | 102 | ### Bug Fixes 103 | 104 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.55 ([6e723a1](https://github.com/kpfromer/nestjs-typegoose/commit/6e723a1f7ded6469794af5ad2cfd83479e58e5bb)) 105 | 106 | ## [7.1.23](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.22...v7.1.23) (2020-05-19) 107 | 108 | 109 | ### Bug Fixes 110 | 111 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.55 ([9f26da0](https://github.com/kpfromer/nestjs-typegoose/commit/9f26da01a3ff607a2c6b9088531924381fa20b34)) 112 | 113 | ## [7.1.22](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.21...v7.1.22) (2020-04-28) 114 | 115 | 116 | ### Bug Fixes 117 | 118 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.54 ([9e40b02](https://github.com/kpfromer/nestjs-typegoose/commit/9e40b0293d3a87a5534016fa966b8aeac7e7fbc4)) 119 | 120 | ## [7.1.21](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.20...v7.1.21) (2020-04-28) 121 | 122 | 123 | ### Bug Fixes 124 | 125 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.54 ([919c463](https://github.com/kpfromer/nestjs-typegoose/commit/919c46309efc95460ea6cb5932ab131302802805)) 126 | 127 | ## [7.1.20](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.19...v7.1.20) (2020-04-27) 128 | 129 | 130 | ### Bug Fixes 131 | 132 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.53 ([f448f85](https://github.com/kpfromer/nestjs-typegoose/commit/f448f8565798f37f5e6feb0552c4e305f762f424)) 133 | 134 | ## [7.1.19](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.18...v7.1.19) (2020-04-27) 135 | 136 | 137 | ### Bug Fixes 138 | 139 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.53 ([a40d264](https://github.com/kpfromer/nestjs-typegoose/commit/a40d26466dd0674a0e37405a0e1265d436e3e40f)) 140 | 141 | ## [7.1.18](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.17...v7.1.18) (2020-04-27) 142 | 143 | 144 | ### Bug Fixes 145 | 146 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.51 ([8083af2](https://github.com/kpfromer/nestjs-typegoose/commit/8083af254ae484bf408e16c397cee53afce436f5)) 147 | 148 | ## [7.1.17](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.16...v7.1.17) (2020-04-27) 149 | 150 | 151 | ### Bug Fixes 152 | 153 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.51 ([7444206](https://github.com/kpfromer/nestjs-typegoose/commit/7444206ee0b46c535b7571099f1771519084d897)) 154 | 155 | ## [7.1.16](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.15...v7.1.16) (2020-04-23) 156 | 157 | 158 | ### Bug Fixes 159 | 160 | * **deps:** update dependency class-validator to v0.12.2 ([ab1211c](https://github.com/kpfromer/nestjs-typegoose/commit/ab1211c66941277642554f1116c6a4afbf40732c)) 161 | 162 | ## [7.1.15](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.14...v7.1.15) (2020-04-23) 163 | 164 | 165 | ### Bug Fixes 166 | 167 | * **deps:** update dependency class-validator to v0.12.0 ([d25ddd1](https://github.com/kpfromer/nestjs-typegoose/commit/d25ddd1a7348991f2a4f539f81ff5426b5e9013a)) 168 | * **deps:** update dependency class-validator to v0.12.1 ([59d4833](https://github.com/kpfromer/nestjs-typegoose/commit/59d48339f591686f53077d160a63ef41881b68ac)) 169 | 170 | ## [7.1.14](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.13...v7.1.14) (2020-04-03) 171 | 172 | 173 | ### Bug Fixes 174 | 175 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.50 ([e902df5](https://github.com/kpfromer/nestjs-typegoose/commit/e902df530935854edb3a6e6b48e236008593d759)) 176 | 177 | ## [7.1.13](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.12...v7.1.13) (2020-04-02) 178 | 179 | 180 | ### Bug Fixes 181 | 182 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.50 ([9709344](https://github.com/kpfromer/nestjs-typegoose/commit/9709344ea2bcde790a5820f5d7a55af64254173e)) 183 | 184 | ## [7.1.12](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.11...v7.1.12) (2020-04-01) 185 | 186 | 187 | ### Bug Fixes 188 | 189 | * **deps:** update dependency @docusaurus/preset-classic to v2.0.0-alpha.49 ([67af1bc](https://github.com/kpfromer/nestjs-typegoose/commit/67af1bc529b842ebd18927495e50e5bdd5dc1fc1)) 190 | 191 | ## [7.1.11](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.10...v7.1.11) (2020-04-01) 192 | 193 | 194 | ### Bug Fixes 195 | 196 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.49 ([a320087](https://github.com/kpfromer/nestjs-typegoose/commit/a320087a8f3f34a2e2ba4ad0b3f018676a29b100)) 197 | 198 | ## [7.1.10](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.9...v7.1.10) (2020-03-30) 199 | 200 | 201 | ### Bug Fixes 202 | 203 | * **deps:** update dependency class-validator to v0.11.1 ([c3ec083](https://github.com/kpfromer/nestjs-typegoose/commit/c3ec08391bd0f2ad5c9c67da0e29a4c6636e39d3)) 204 | 205 | ## [7.1.9](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.8...v7.1.9) (2020-03-28) 206 | 207 | 208 | ### Bug Fixes 209 | 210 | * **deps:** update dependency nestjs-typegoose to v7.1.8 ([fc392ad](https://github.com/kpfromer/nestjs-typegoose/commit/fc392ad003f6c03d190d699374661dffcc77564d)) 211 | 212 | ## [7.1.8](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.7...v7.1.8) (2020-03-28) 213 | 214 | 215 | ### Bug Fixes 216 | 217 | * **deps:** update dependency nestjs-typegoose to v7.1.7 ([657a8f7](https://github.com/kpfromer/nestjs-typegoose/commit/657a8f726fe4c480ee82e3ff23d199573070c04e)) 218 | 219 | ## [7.1.7](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.6...v7.1.7) (2020-03-28) 220 | 221 | 222 | ### Bug Fixes 223 | 224 | * **deps:** update dependency nestjs-typegoose to v7.1.6 ([4177c5e](https://github.com/kpfromer/nestjs-typegoose/commit/4177c5ed223c6648d715370b1efe81f6b7fdb39a)) 225 | 226 | ## [7.1.6](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.5...v7.1.6) (2020-03-28) 227 | 228 | 229 | ### Bug Fixes 230 | 231 | * **deps:** update dependency nestjs-typegoose to v7.1.5 ([3cf1d0e](https://github.com/kpfromer/nestjs-typegoose/commit/3cf1d0ec8d9ea6ebef7a21ec0b4fbfc39638c8e7)) 232 | 233 | ## [7.1.5](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.4...v7.1.5) (2020-03-28) 234 | 235 | 236 | ### Bug Fixes 237 | 238 | * **deps:** update react monorepo to v16.13.1 ([2d38bef](https://github.com/kpfromer/nestjs-typegoose/commit/2d38bef8ed5705579f33802ea172d45a787be803)) 239 | 240 | ## [7.1.4](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.3...v7.1.4) (2020-03-28) 241 | 242 | 243 | ### Bug Fixes 244 | 245 | * **deps:** update dependency rimraf to v3.0.2 ([315710c](https://github.com/kpfromer/nestjs-typegoose/commit/315710c672f867e8e8472a6282101fb0c3167118)) 246 | 247 | ## [7.1.3](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.2...v7.1.3) (2020-03-28) 248 | 249 | 250 | ### Bug Fixes 251 | 252 | * **deps:** update dependency nestjs-typegoose to v7.1.2 ([22c7408](https://github.com/kpfromer/nestjs-typegoose/commit/22c740856e9708dc11fd676e54a6eaee8dc75a96)) 253 | 254 | ## [7.1.2](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.1...v7.1.2) (2020-03-28) 255 | 256 | 257 | ### Bug Fixes 258 | 259 | * **deps:** update dependency @typegoose/typegoose to v6.4.0 ([03d2eca](https://github.com/kpfromer/nestjs-typegoose/commit/03d2eca017bc662fdd6d195413a745fd2f9063aa)) 260 | 261 | ## [7.1.1](https://github.com/kpfromer/nestjs-typegoose/compare/v7.1.0...v7.1.1) (2020-03-28) 262 | 263 | 264 | ### Bug Fixes 265 | 266 | * **deps:** update dependency @docusaurus/core to v2.0.0-alpha.48 ([6a15f76](https://github.com/kpfromer/nestjs-typegoose/commit/6a15f768f490490198885b015e37be259ee2e7d2)) 267 | 268 | ## [7.1.0] - 2020-2-11 269 | 270 | ### Added 271 | 272 | - Mongoose Discriminators. 273 | 274 | ### Changed 275 | 276 | - Updated peer dependency `@nestjs/common` from version `6.3.1` to `6.10.1` 277 | - Updated peer dependency `@nestjs/core` from version `6.3.1` to `6.10.1` 278 | - Updated peer dependency `@nestjs/core` from version `6.0.0` to `6.2.1` 279 | - Updated peer dependency `mongoose` from version `5.5.13` to `5.8.4` 280 | 281 | ### Fixed 282 | 283 | - Fixed typos in the documentation. 284 | - Database shutdown behavior. (PR #54) 285 | - Clear cache of typegoose. (PR #40) 286 | 287 | ## [7.0.0] - 2019-10-08 288 | 289 | ### Changed 290 | 291 | - **BREAKING CHANGE**: `nest-typegoose` uses `@typegoose/typegoose` as a peer dependency instead of `@hasezoey/typegoose` now. 292 | 293 | ## [6.0.0] - 2019-09-29 294 | 295 | ### Changed 296 | 297 | - **BREAKING CHANGE**: `nestjs-typegoose` uses `@hasezoey/typegoose` as a peer dependency instead of `typegoose` now. 298 | 299 | ## [5.2.0] - 2019-06-06 300 | 301 | ### Added 302 | 303 | - Multiple database connections. 304 | 305 | ### Changed 306 | 307 | - **BREAKING CHANGE:** models are no long spread for `forFeature`. For example: 308 | Before: 309 | 310 | ```typescript 311 | @Module({ 312 | imports: [TypegooseModule.forFeature(Cat, OtherModel)], 313 | controllers: [CatsController], 314 | providers: [CatsService] 315 | }) 316 | export class CatsModule {} 317 | ``` 318 | 319 | After: 320 | 321 | ```typescript 322 | @Module({ 323 | imports: [TypegooseModule.forFeature([Cat, OtherModel])], 324 | controllers: [CatsController], 325 | providers: [CatsService] 326 | }) 327 | export class CatsModule {} 328 | ``` 329 | 330 | ### Fixed 331 | 332 | - `TypegooseModule` will disconnect from the monodb server on module destruction. 333 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, or any other method with the owners of this repository before making a change. 5 | 6 | Please note we have a code of conduct, please follow it in all your interactions with the project. 7 | 8 | ## Pull Request Process 9 | 10 | 1. Ensure any install or build dependencies are removed before the end of the layer when doing a 11 | build. 12 | 2. Update the README.md with details of changes to the interface, this includes new environment 13 | variables, exposed ports, useful file locations and container parameters. 14 | 3. Increase the version numbers in any examples files and the README.md to the new version that this 15 | Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). 16 | 4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you 17 | do not have permission to do that, you may request the second reviewer to merge it for you. 18 | 19 | ## Code of Conduct 20 | 21 | ### Our Pledge 22 | 23 | In the interest of fostering an open and welcoming environment, we as 24 | contributors and maintainers pledge to making participation in our project and 25 | our community a harassment-free experience for everyone, regardless of age, body 26 | size, disability, ethnicity, gender identity and expression, level of experience, 27 | nationality, personal appearance, race, religion, or sexual identity and 28 | orientation. 29 | 30 | ### Our Standards 31 | 32 | Examples of behavior that contributes to creating a positive environment 33 | include: 34 | 35 | * Using welcoming and inclusive language 36 | * Being respectful of differing viewpoints and experiences 37 | * Gracefully accepting constructive criticism 38 | * Focusing on what is best for the community 39 | * Showing empathy towards other community members 40 | 41 | Examples of unacceptable behavior by participants include: 42 | 43 | * The use of sexualized language or imagery and unwelcome sexual attention or 44 | advances 45 | * Trolling, insulting/derogatory comments, and personal or political attacks 46 | * Public or private harassment 47 | * Publishing others' private information, such as a physical or electronic 48 | address, without explicit permission 49 | * Other conduct which could reasonably be considered inappropriate in a 50 | professional setting 51 | 52 | ### Our Responsibilities 53 | 54 | Project maintainers are responsible for clarifying the standards of acceptable 55 | behavior and are expected to take appropriate and fair corrective action in 56 | response to any instances of unacceptable behavior. 57 | 58 | Project maintainers have the right and responsibility to remove, edit, or 59 | reject comments, commits, code, wiki edits, issues, and other contributions 60 | that are not aligned to this Code of Conduct, or to ban temporarily or 61 | permanently any contributor for other behaviors that they deem inappropriate, 62 | threatening, offensive, or harmful. 63 | 64 | ### Scope 65 | 66 | This Code of Conduct applies both within project spaces and in public spaces 67 | when an individual is representing the project or its community. Examples of 68 | representing a project or community include using an official project e-mail 69 | address, posting via an official social media account, or acting as an appointed 70 | representative at an online or offline event. Representation of a project may be 71 | further defined and clarified by project maintainers. 72 | 73 | ### Enforcement 74 | 75 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 76 | reported by contacting the project team at [kpfromer2@gmail.com]. All 77 | complaints will be reviewed and investigated and will result in a response that 78 | is deemed necessary and appropriate to the circumstances. The project team is 79 | obligated to maintain confidentiality with regard to the reporter of an incident. 80 | Further details of specific enforcement policies may be posted separately. 81 | 82 | Project maintainers who do not follow or enforce the Code of Conduct in good 83 | faith may face temporary or permanent repercussions as determined by other 84 | members of the project's leadership. 85 | 86 | ### Attribution 87 | 88 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 89 | available at [http://contributor-covenant.org/version/1/4][version] 90 | 91 | [homepage]: http://contributor-covenant.org 92 | [version]: http://contributor-covenant.org/version/1/4/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Kyle Pfromer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nestjs-typegoose 2 | 3 | [![NPM](https://nodei.co/npm/nestjs-typegoose.png)](https://nodei.co/npm/nestjs-typegoose/) 4 | 5 | [![npm version](https://badge.fury.io/js/nestjs-typegoose.svg)](https://badge.fury.io/js/nestjs-typegoose) 6 | [![Build Status](https://travis-ci.org/kpfromer/nestjs-typegoose.svg?branch=master)](https://travis-ci.org/kpfromer/nestjs-typegoose) 7 | [![Coverage Status](https://coveralls.io/repos/github/kpfromer/nestjs-typegoose/badge.svg?branch=master)](https://coveralls.io/github/kpfromer/nestjs-typegoose?branch=master) 8 | ![npm](https://img.shields.io/npm/dm/nestjs-typegoose) 9 | ![npm bundle size](https://img.shields.io/bundlephobia/min/nestjs-typegoose) 10 | ![David](https://img.shields.io/david/peer/kpfromer/nestjs-typegoose) 11 | 12 | ## Description 13 | 14 | Injects [typegoose](https://github.com/szokodiakos/typegoose) models for [nest](https://github.com/nestjs/nest) components and controllers. Typegoose equivalant for [@nestjs/mongoose.](https://docs.nestjs.com/techniques/mongodb) 15 | 16 | Using Typegoose removes the need for having a Model interface. 17 | 18 | ## Installation 19 | 20 | ```bash 21 | npm install --save nestjs-typegoose 22 | ``` 23 | 24 | or 25 | 26 | ``` 27 | yarn add nestjs-typegoose 28 | ``` 29 | 30 | ## Documentation 31 | 32 | [Here is the full documentation describing all basic and advanced features.](https://kpfromer.github.io/nestjs-typegoose/) 33 | 34 | ## Basic usage 35 | 36 | You can checkout the `example` project for more details. 37 | 38 | **app.module.ts** 39 | 40 | ```typescript 41 | import { Module } from "@nestjs/common"; 42 | import { TypegooseModule } from "nestjs-typegoose"; 43 | import { CatsModule } from "./cat.module.ts"; 44 | 45 | @Module({ 46 | imports: [ 47 | TypegooseModule.forRoot("mongodb://localhost:27017/nest", { 48 | useNewUrlParser: true, 49 | }), 50 | CatsModule, 51 | ], 52 | }) 53 | export class ApplicationModule {} 54 | ``` 55 | 56 | Create class that describes your schema 57 | 58 | **cat.model.ts** 59 | 60 | ```typescript 61 | import { prop } from "@typegoose/typegoose"; 62 | import { IsString } from "class-validator"; 63 | 64 | export class Cat { 65 | @IsString() 66 | @prop({ required: true }) 67 | name: string; 68 | } 69 | ``` 70 | 71 | Inject Cat for `CatsModule` 72 | 73 | **cat.module.ts** 74 | 75 | ```typescript 76 | import { Module } from "@nestjs/common"; 77 | import { TypegooseModule } from "nestjs-typegoose"; 78 | import { Cat } from "./cat.model"; 79 | import { CatsController } from "./cats.controller"; 80 | import { CatsService } from "./cats.service"; 81 | 82 | @Module({ 83 | imports: [TypegooseModule.forFeature([Cat])], 84 | controllers: [CatsController], 85 | providers: [CatsService], 86 | }) 87 | export class CatsModule {} 88 | ``` 89 | 90 | Get the cat model in a service 91 | 92 | **cats.service.ts** 93 | 94 | ```typescript 95 | import { Injectable } from "@nestjs/common"; 96 | import { InjectModel } from "nestjs-typegoose"; 97 | import { Cat } from "./cat.model"; 98 | import { ReturnModelType } from "@typegoose/typegoose"; 99 | 100 | @Injectable() 101 | export class CatsService { 102 | constructor( 103 | @InjectModel(Cat) private readonly catModel: ReturnModelType 104 | ) {} 105 | 106 | async create(createCatDto: { name: string }): Promise { 107 | const createdCat = new this.catModel(createCatDto); 108 | return await createdCat.save(); 109 | } 110 | 111 | async findAll(): Promise { 112 | return await this.catModel.find().exec(); 113 | } 114 | } 115 | ``` 116 | 117 | Finally, use the service in a controller! 118 | 119 | **cats.controller.ts** 120 | 121 | ```typescript 122 | import { Controller, Get, Post, Body } from "@nestjs/common"; 123 | import { CatsService } from "./cats.service"; 124 | import { Cat } from "./cats.model.ts"; 125 | 126 | @Controller("cats") 127 | export class CatsController { 128 | constructor(private readonly catsService: CatsService) {} 129 | 130 | @Get() 131 | async getCats(): Promise { 132 | return await this.catsService.findAll(); 133 | } 134 | 135 | @Post() 136 | async create(@Body() cat: Cat): Promise { 137 | return await this.catsService.create(cat); 138 | } 139 | } 140 | ``` 141 | 142 | ## Requirements 143 | 144 | 1. @typegoose/typegoose +6.1.5 145 | 2. @nestjs/common +6.10.1 146 | 3. @nestjs/core +6.10.1 147 | 4. mongoose (with typings `@types/mongoose`) +5.7.12 148 | 149 | ## License 150 | 151 | nestjs-typegoose is [MIT licensed](LICENSE). 152 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { extends: ["@commitlint/config-conventional"] }; 2 | -------------------------------------------------------------------------------- /example/nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "license": "MIT", 7 | "scripts": { 8 | "prebuild": "rimraf dist", 9 | "build": "nest build", 10 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 11 | "start": "nest start", 12 | "start:dev": "nest start --watch", 13 | "start:debug": "nest start --debug --watch", 14 | "start:prod": "node dist/main", 15 | "lint": "tslint -p tsconfig.json -c tslint.json", 16 | "test": "jest", 17 | "test:watch": "jest --watch", 18 | "test:cov": "jest --coverage", 19 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 20 | "test:e2e": "jest --config ./test/jest-e2e.json" 21 | }, 22 | "dependencies": { 23 | "@nestjs/common": "7.4.4", 24 | "@nestjs/core": "7.4.4", 25 | "@nestjs/platform-express": "7.4.4", 26 | "@typegoose/typegoose": "6.5.0", 27 | "class-validator": "0.12.2", 28 | "mongoose": "5.10.4", 29 | "nestjs-typegoose": "7.1.8", 30 | "reflect-metadata": "0.1.13", 31 | "rimraf": "3.0.2", 32 | "rxjs": "6.6.3" 33 | }, 34 | "devDependencies": { 35 | "@nestjs/cli": "7.5.1", 36 | "@nestjs/schematics": "6.9.4", 37 | "@nestjs/testing": "7.4.4", 38 | "@types/express": "4.17.8", 39 | "@types/jest": "26.0.13", 40 | "@types/mongoose": "5.7.36", 41 | "@types/node": "12.12.56", 42 | "@types/supertest": "2.0.10", 43 | "jest": "26.4.2", 44 | "mongodb-memory-server": "6.7.1", 45 | "prettier": "2.1.1", 46 | "supertest": "4.0.2", 47 | "ts-jest": "26.3.0", 48 | "ts-loader": "8.0.3", 49 | "ts-node": "8.10.2", 50 | "tsconfig-paths": "3.9.0", 51 | "tslint": "5.20.1", 52 | "typescript": "3.9.7" 53 | }, 54 | "jest": { 55 | "moduleFileExtensions": [ 56 | "js", 57 | "json", 58 | "ts" 59 | ], 60 | "rootDir": "src", 61 | "testRegex": ".spec.ts$", 62 | "transform": { 63 | "^.+\\.(t|j)s$": "ts-jest" 64 | }, 65 | "coverageDirectory": "../coverage", 66 | "testEnvironment": "node" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /example/src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /example/src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | import { TypegooseModule } from 'nestjs-typegoose'; 5 | import { CatsModule } from './cat/cats.module'; 6 | 7 | @Module({ 8 | imports: [ 9 | TypegooseModule.forRoot('mongodb://localhost:27017/nest', { 10 | useNewUrlParser: true, 11 | }), 12 | CatsModule, 13 | ], 14 | controllers: [AppController], 15 | providers: [AppService], 16 | }) 17 | export class AppModule {} 18 | -------------------------------------------------------------------------------- /example/src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /example/src/cat/cat.model.ts: -------------------------------------------------------------------------------- 1 | import { prop } from '@typegoose/typegoose'; 2 | import { IsString } from 'class-validator'; 3 | 4 | export class Cat { 5 | @IsString() 6 | @prop({ required: true }) 7 | name: string; 8 | } 9 | -------------------------------------------------------------------------------- /example/src/cat/cats.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body } from '@nestjs/common'; 2 | import { CatsService } from './cats.service'; 3 | import { Cat } from './cat.model'; 4 | 5 | @Controller('cats') 6 | export class CatsController { 7 | constructor(private readonly catsService: CatsService) {} 8 | 9 | @Get() 10 | async getCats(): Promise { 11 | return await this.catsService.findAll(); 12 | } 13 | 14 | @Post() 15 | async create(@Body() cat: Cat): Promise { 16 | return await this.catsService.create(cat); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/src/cat/cats.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypegooseModule } from 'nestjs-typegoose'; 3 | import { Cat } from './cat.model'; 4 | import { CatsController } from './cats.controller'; 5 | import { CatsService } from './cats.service'; 6 | 7 | @Module({ 8 | imports: [TypegooseModule.forFeature([Cat])], 9 | controllers: [CatsController], 10 | providers: [CatsService], 11 | }) 12 | export class CatsModule {} 13 | -------------------------------------------------------------------------------- /example/src/cat/cats.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Cat } from "./cat.model"; 2 | import { CatsService } from "./cats.service"; 3 | import { TestingModule, Test } from "@nestjs/testing"; 4 | import { TypegooseModule } from "nestjs-typegoose"; 5 | import { TestDatabaseModule } from "../database/test-database.module"; 6 | 7 | const testCat = { 8 | name: "Mica" 9 | } 10 | 11 | describe('CatsService', () => { 12 | let module: TestingModule; 13 | let service: CatsService; 14 | 15 | beforeAll(async () => { 16 | module = await Test.createTestingModule({ 17 | imports: [ 18 | TestDatabaseModule, 19 | TypegooseModule.forFeature([Cat]) 20 | ], 21 | providers: [CatsService], 22 | }).compile(); 23 | 24 | service = module.get(CatsService); 25 | }); 26 | 27 | afterAll(async () => { 28 | await module.close(); 29 | }); 30 | 31 | it('should be defined', () => { 32 | expect(service).toBeDefined(); 33 | }); 34 | 35 | it('should insert a new cat', async () => { 36 | const newCat = await service.create(testCat); 37 | expect(newCat.name).toEqual('Mica'); 38 | }); 39 | 40 | it('should return all cats', async () => { 41 | await expect(service.findAll()).resolves.toHaveLength(1); 42 | await service.create(testCat); 43 | await expect(service.findAll()).resolves.toHaveLength(2); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /example/src/cat/cats.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectModel } from 'nestjs-typegoose'; 3 | import { Cat } from './cat.model'; 4 | import { ReturnModelType } from '@typegoose/typegoose'; 5 | 6 | @Injectable() 7 | export class CatsService { 8 | constructor( 9 | @InjectModel(Cat) private readonly catModel: ReturnModelType, 10 | ) {} 11 | 12 | async create(createCatDto: { name: string }): Promise { 13 | return await this.catModel.create(createCatDto); 14 | } 15 | 16 | async findAll(): Promise { 17 | return await this.catModel.find().exec(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/src/database/test-database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypegooseModule } from 'nestjs-typegoose'; 3 | import { MongoMemoryServer } from 'mongodb-memory-server'; 4 | 5 | @Module({ 6 | imports: [ 7 | TypegooseModule.forRootAsync({ 8 | useFactory: async () => { 9 | const mongod = new MongoMemoryServer(); 10 | return { 11 | uri: await mongod.getConnectionString(), 12 | useNewUrlParser: true, 13 | useUnifiedTopology: true, 14 | useCreateIndex: true, 15 | }; 16 | }, 17 | }), 18 | ], 19 | }) 20 | export class TestDatabaseModule {} 21 | -------------------------------------------------------------------------------- /example/src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from "@nestjs/core"; 2 | import { AppModule } from "./app.module"; 3 | 4 | (async function() { 5 | const app = await NestFactory.create(AppModule); 6 | await app.listen(3000); 7 | })(); 8 | -------------------------------------------------------------------------------- /example/src/user/user.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Post, Body } from '@nestjs/common'; 2 | import { UserService } from './user.service'; 3 | import { User } from './user.model'; 4 | 5 | @Controller('users') 6 | export class UserController { 7 | constructor(private readonly userService: UserService) {} 8 | 9 | @Get() 10 | async getUser(): Promise { 11 | return await this.userService.findAll(); 12 | } 13 | 14 | @Post() 15 | async create(@Body() user: User): Promise { 16 | return await this.userService.create(user); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/src/user/user.model.ts: -------------------------------------------------------------------------------- 1 | import { prop, arrayProp, Ref } from '@typegoose/typegoose'; 2 | import { Cat } from 'src/cat/cat.model'; 3 | 4 | export class User { 5 | @prop({ required: true }) 6 | public name: string; 7 | 8 | @arrayProp({ required: true, items: Cat }) 9 | public cats: Ref; 10 | } 11 | -------------------------------------------------------------------------------- /example/src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypegooseModule } from 'nestjs-typegoose'; 3 | import { UserService } from './user.service'; 4 | import { UserController } from './user.controller'; 5 | import { User } from './user.model'; 6 | 7 | @Module({ 8 | imports: [TypegooseModule.forFeature([User])], 9 | controllers: [UserController], 10 | providers: [UserService] 11 | }) 12 | export class UserModule {} 13 | -------------------------------------------------------------------------------- /example/src/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectModel } from 'nestjs-typegoose'; 3 | import { User } from './user.model'; 4 | import { ReturnModelType } from '@typegoose/typegoose'; 5 | 6 | @Injectable() 7 | export class UserService { 8 | constructor( 9 | @InjectModel(User) private readonly userModel: ReturnModelType 10 | ) {} 11 | 12 | async create(createCatDto: { name: string }): Promise { 13 | return await this.userModel.create(createCatDto); 14 | } 15 | 16 | async findAll(): Promise { 17 | return await this.userModel.find().exec(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "target": "es2017", 9 | "sourceMap": true, 10 | "outDir": "./dist", 11 | "baseUrl": "./", 12 | "incremental": true 13 | }, 14 | "exclude": ["node_modules", "dist"] 15 | } 16 | -------------------------------------------------------------------------------- /example/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint:recommended"], 4 | "jsRules": { 5 | "no-unused-expression": true 6 | }, 7 | "rules": { 8 | "quotemark": [true, "single"], 9 | "member-access": [false], 10 | "ordered-imports": [false], 11 | "max-line-length": [true, 150], 12 | "member-ordering": [false], 13 | "interface-name": [false], 14 | "arrow-parens": false, 15 | "object-literal-sort-keys": false 16 | }, 17 | "rulesDirectory": [] 18 | } 19 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | const gulp = require('gulp'); 2 | const ts = require('gulp-typescript'); 3 | 4 | const tsProject = ts.createProject('tsconfig.json'); 5 | 6 | const build = () => 7 | gulp.src('src/**/*.ts').pipe(tsProject()).pipe(gulp.dest('dist')); 8 | 9 | module.exports = {build}; 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-typegoose", 3 | "version": "7.1.38", 4 | "description": "A nestjs module wrapper for typegoose", 5 | "main": "dist/index.js", 6 | "types": "dist/index.d.ts", 7 | "engines": { 8 | "node": ">=8.10.0" 9 | }, 10 | "scripts": { 11 | "build": "tsc -p tsconfig.build.json", 12 | "lint": "tslint --project tsconfig.json", 13 | "test": "jest", 14 | "test:ci": "jest", 15 | "test:cov": "jest --coverage", 16 | "test:e2e": "jest --config ./test/jest-e2e.json", 17 | "test:watch": "jest --watch", 18 | "coverage": "yarn run test:cov && cat ./coverage/lcov.info | coveralls", 19 | "prepare": "npm run build", 20 | "commit": "git cz" 21 | }, 22 | "author": "Kyle Pfromer", 23 | "license": "MIT", 24 | "repository": "github:kpfromer/nestjs-typegoose", 25 | "peerDependencies": { 26 | "@nestjs/common": "^6.10.1 || ^7.0.0", 27 | "@nestjs/core": "^6.10.1 || ^7.0.0", 28 | "@typegoose/typegoose": "^6.2.1 || ^7.0.0", 29 | "mongoose": "^5.10.6" 30 | }, 31 | "dependencies": { 32 | "is-class": "^0.0.9", 33 | "reflect-metadata": "^0.1.13" 34 | }, 35 | "devDependencies": { 36 | "@commitlint/cli": "9.1.2", 37 | "@commitlint/config-conventional": "9.1.2", 38 | "@nestjs/common": "7.4.4", 39 | "@nestjs/core": "7.4.4", 40 | "@nestjs/platform-express": "7.4.4", 41 | "@nestjs/testing": "7.4.4", 42 | "@semantic-release/changelog": "5.0.1", 43 | "@semantic-release/commit-analyzer": "8.0.1", 44 | "@semantic-release/git": "9.0.0", 45 | "@semantic-release/npm": "7.0.5", 46 | "@semantic-release/release-notes-generator": "9.0.1", 47 | "@typegoose/typegoose": "7.4.0", 48 | "@types/jest": "26.0.13", 49 | "@types/mongodb": "^3.5.27", 50 | "@types/mongoose": "5.7.36", 51 | "@types/supertest": "2.0.10", 52 | "commitizen": "4.2.1", 53 | "coveralls": "3.1.0", 54 | "cz-conventional-changelog": "3.3.0", 55 | "husky": "4.3.0", 56 | "jest": "26.4.2", 57 | "mongodb-memory-server": "6.7.1", 58 | "mongoose": "5.10.4", 59 | "rxjs": "6.6.3", 60 | "semantic-release": "17.1.1", 61 | "supertest": "latest", 62 | "ts-jest": "26.3.0", 63 | "tslint": "5.20.1", 64 | "typescript": "3.9.7" 65 | }, 66 | "jest": { 67 | "moduleFileExtensions": [ 68 | "js", 69 | "ts" 70 | ], 71 | "rootDir": "src", 72 | "testRegex": ".spec.ts$", 73 | "transform": { 74 | "^.+\\.(t|j)s$": "ts-jest" 75 | }, 76 | "coverageDirectory": "../coverage", 77 | "resetMocks": true, 78 | "testEnvironment": "node" 79 | }, 80 | "release": { 81 | "plugins": [ 82 | "@semantic-release/commit-analyzer", 83 | "@semantic-release/release-notes-generator", 84 | "@semantic-release/npm", 85 | "@semantic-release/changelog", 86 | "@semantic-release/git" 87 | ], 88 | "branch": "master" 89 | }, 90 | "config": { 91 | "commitizen": { 92 | "path": "./node_modules/cz-conventional-changelog" 93 | } 94 | }, 95 | "husky": { 96 | "hooks": { 97 | "pre-commit": "yarn test", 98 | "pre-push": "yarn test", 99 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/__snapshots__/typegoose-core.module.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`TypegooseCoreModule forAsyncRoot different types useFactory injects the factory's async options into DEFAULT_DB_CONNECTION_NAME 1`] = ` 4 | Array [ 5 | Object { 6 | "inject": Array [ 7 | "CONFIG_SERVICE", 8 | ], 9 | "provide": "TypegooseModuleOptions", 10 | "useFactory": [MockFunction], 11 | }, 12 | Object { 13 | "inject": Array [ 14 | "TypegooseModuleOptions", 15 | ], 16 | "provide": "DefaultTypegooseConnection", 17 | "useFactory": [Function], 18 | }, 19 | Object { 20 | "provide": "TypegooseConnectionName", 21 | "useValue": "DefaultTypegooseConnection", 22 | }, 23 | ] 24 | `; 25 | 26 | exports[`TypegooseCoreModule forAsyncRoot different types useFactory injects the factory's async options into DEFAULT_DB_CONNECTION_NAME 2`] = ` 27 | Array [ 28 | Object { 29 | "inject": Array [ 30 | "TypegooseModuleOptions", 31 | ], 32 | "provide": "DefaultTypegooseConnection", 33 | "useFactory": [Function], 34 | }, 35 | ] 36 | `; -------------------------------------------------------------------------------- /src/__snapshots__/typegoose.providers.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`convertToTypegooseClassWithOptions throws error if a discriminator is not a class or TypegooseDiscriminator 1`] = `"Invalid discriminator object"`; 4 | 5 | exports[`convertToTypegooseClassWithOptions throws error is not a class or not a TypegooseClassWithOptions 1`] = `"Invalid model object"`; 6 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './typegoose.decorators'; 2 | export * from './typegoose.module'; 3 | export * from './typegoose.utils'; 4 | export * from './typegoose-options.interface'; 5 | -------------------------------------------------------------------------------- /src/typegoose-class.interface.ts: -------------------------------------------------------------------------------- 1 | import { SchemaOptions } from 'mongoose'; 2 | 3 | export interface TypegooseClass { 4 | new (...args: any[]); 5 | } 6 | 7 | export interface TypegooseClassWrapper { 8 | typegooseClass: TypegooseClass; 9 | } 10 | 11 | export interface TypegooseClassWithOptions extends TypegooseClassWrapper { 12 | schemaOptions?: SchemaOptions; 13 | discriminators?: (TypegooseClass | TypegooseDiscriminator)[]; 14 | } 15 | 16 | export interface TypegooseDiscriminator extends TypegooseClassWrapper { 17 | discriminatorId?: string; 18 | } 19 | -------------------------------------------------------------------------------- /src/typegoose-core.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { TypegooseCoreModule } from './typegoose-core.module'; 2 | import * as mongoose from 'mongoose'; 3 | import { DEFAULT_DB_CONNECTION_NAME, TYPEGOOSE_MODULE_OPTIONS, TYPEGOOSE_CONNECTION_NAME } from './typegoose.constants'; 4 | import { DynamicModule } from '@nestjs/common'; 5 | import { FactoryProvider, ClassProvider } from '@nestjs/common/interfaces'; 6 | 7 | describe('TypegooseCoreModule', () => { 8 | describe('forRoot', () => { 9 | it('should return module that provides a mongoose connection', () => { 10 | const connection = 'i am a connection'; 11 | 12 | jest.spyOn(mongoose, 'createConnection').mockReturnValue(connection as any); 13 | 14 | const module = TypegooseCoreModule.forRoot('mongouri', {authdb: 'mongo connection'} as any); 15 | 16 | const connectionNameProvider = { 17 | provide: TYPEGOOSE_CONNECTION_NAME, 18 | useValue: DEFAULT_DB_CONNECTION_NAME 19 | }; 20 | 21 | const connectionProvider = { 22 | provide: DEFAULT_DB_CONNECTION_NAME, 23 | useFactory: expect.any(Function) 24 | }; 25 | 26 | expect(module).toEqual({ 27 | module: TypegooseCoreModule, 28 | providers: [connectionProvider, connectionNameProvider], 29 | exports: [connectionProvider] 30 | }); 31 | 32 | const dbProvider = module.exports[0] as FactoryProvider; 33 | 34 | expect(dbProvider.useFactory()).toBe(connection); 35 | }); 36 | 37 | it('should create connection with no mongoose config', () => { 38 | const connection = 'i am a connection'; 39 | 40 | jest.spyOn(mongoose, 'createConnection').mockReturnValue(connection as any); 41 | 42 | const module = TypegooseCoreModule.forRoot('mongouri'); 43 | 44 | (module.exports[0] as FactoryProvider).useFactory(); 45 | 46 | expect(mongoose.createConnection).toHaveBeenCalledWith('mongouri', {}); 47 | }); 48 | }); 49 | describe('forAsyncRoot', () => { 50 | let connection, mockOptionFactory, wantedDependencies, module: DynamicModule; 51 | 52 | beforeEach(() => { 53 | connection = 'i am a connection'; 54 | 55 | jest.spyOn(mongoose, 'createConnection').mockReturnValue(connection); 56 | 57 | mockOptionFactory = jest.fn(); 58 | wantedDependencies = ['CONFIG_SERVICE']; 59 | }); 60 | 61 | describe('Connection Name', () => { 62 | let DbConnectionToken: FactoryProvider; 63 | beforeEach(() => { 64 | module = TypegooseCoreModule.forRootAsync({ 65 | useFactory: () => 'testing' 66 | } as any); 67 | DbConnectionToken = module.exports[0] as FactoryProvider; 68 | }); 69 | it('is the only export of the returned module', () => { 70 | expect(module.exports.length).toBe(1); 71 | expect(module.exports[0]).toMatchObject({ 72 | provide: DEFAULT_DB_CONNECTION_NAME 73 | }); 74 | }); 75 | it('injects the TYPEGOOSE_MODULE_OPTIONS config', () => { 76 | expect(DbConnectionToken.inject).toEqual([TYPEGOOSE_MODULE_OPTIONS]); 77 | }); 78 | it('creates the mongoose connection', () => { 79 | const optionsFromOptionFactory = { 80 | uri: 'uriForMongoose', 81 | other: 'options', 82 | can: 'work' 83 | }; 84 | expect(DbConnectionToken.useFactory(optionsFromOptionFactory)).toBe(connection); 85 | expect(mongoose.createConnection).toHaveBeenCalledWith(optionsFromOptionFactory.uri, { 86 | other: 'options', 87 | can: 'work' 88 | }); 89 | }); 90 | }); 91 | 92 | describe('different types', () => { 93 | describe('useFactory', () => { 94 | beforeEach(() => { 95 | module = TypegooseCoreModule.forRootAsync({ 96 | useFactory: mockOptionFactory, 97 | inject: wantedDependencies 98 | }); 99 | }); 100 | it('injects the factory\'s async options into DEFAULT_DB_CONNECTION_NAME', () => { 101 | expect(module.providers).toMatchSnapshot(); 102 | expect(module.exports).toMatchSnapshot(); 103 | const typegooseModuleOptionsFactoryProvider = 104 | module.providers.find(provider => 105 | (provider as FactoryProvider).provide === TYPEGOOSE_MODULE_OPTIONS 106 | ) as FactoryProvider; 107 | expect(typegooseModuleOptionsFactoryProvider.inject).toBe(wantedDependencies); 108 | expect(typegooseModuleOptionsFactoryProvider.useFactory).toBe(mockOptionFactory); 109 | }); 110 | }); 111 | 112 | describe('useClass', () => { 113 | let mockConfigClass; 114 | beforeEach(() => { 115 | mockConfigClass = { 116 | createTypegooseOptions: jest.fn() 117 | }; 118 | 119 | module = TypegooseCoreModule.forRootAsync({ 120 | useClass: mockConfigClass 121 | }); 122 | }); 123 | it('provides the TypegooseConfigService class for TYPEGOOSE_MODULE_OPTIONS', () => { 124 | const classProvider = 125 | module.providers.find(provider => 126 | (provider as ClassProvider).provide === mockConfigClass 127 | ) as ClassProvider; 128 | expect(classProvider.provide).toBe(mockConfigClass); 129 | expect(classProvider.useClass).toBe(mockConfigClass); 130 | }); 131 | it('creates a factory called TYPEGOOSE_MODULE_OPTIONS that calls TypegooseConfigService\'s createMongooseOptions', async () => { 132 | const typegooseModuleOptionsFactoryProvider = 133 | module.providers.find(provider => 134 | (provider as FactoryProvider).provide === TYPEGOOSE_MODULE_OPTIONS 135 | ) as FactoryProvider; 136 | // The factory needs to get the class 137 | expect(typegooseModuleOptionsFactoryProvider.inject).toEqual([mockConfigClass]); 138 | // Then provides wrapper factory, which will get injected TypegooseConfigService 139 | await typegooseModuleOptionsFactoryProvider.useFactory(mockConfigClass); 140 | expect(mockConfigClass.createTypegooseOptions).toHaveBeenCalled(); 141 | }); 142 | }); 143 | 144 | describe('useExisting', () => { 145 | let mockUseExistingClass; 146 | 147 | beforeEach(() => { 148 | mockUseExistingClass = jest.fn(); 149 | module = TypegooseCoreModule.forRootAsync({ 150 | useExisting: mockUseExistingClass 151 | }); 152 | }); 153 | 154 | it('injects the useExisting class into the TYPEGOOSE_MODULE_OPTIONS factory', () => { 155 | const typegooseModuleOptionsFactoryProvider = 156 | module.providers.find(provider => 157 | (provider as FactoryProvider).provide === TYPEGOOSE_MODULE_OPTIONS 158 | ) as FactoryProvider; 159 | expect(typegooseModuleOptionsFactoryProvider.inject).toEqual([mockUseExistingClass]); 160 | }); 161 | }); 162 | }); 163 | }); 164 | 165 | describe('Disconnect in onModuleDestroy', () => { 166 | it('should close connection while destroying module', async () => { 167 | const closeMock = jest.fn(() => Promise.resolve()); 168 | const moduleRefGet = jest.fn(() => ({ close: closeMock })); 169 | const coreModule = new TypegooseCoreModule(DEFAULT_DB_CONNECTION_NAME, { 170 | get: moduleRefGet 171 | } as any); 172 | 173 | await coreModule.onApplicationShutdown(); 174 | 175 | expect(moduleRefGet).toHaveBeenCalledWith(DEFAULT_DB_CONNECTION_NAME); 176 | expect(closeMock).toHaveBeenCalledTimes(1); 177 | }); 178 | 179 | it('shouldn\'t throw error on destroy when the mongoose connection is not found in ref', async () => { 180 | const coreModule = new TypegooseCoreModule(DEFAULT_DB_CONNECTION_NAME, { 181 | get: () => null, 182 | } as any); 183 | 184 | await expect(() => coreModule.onApplicationShutdown()).not.toThrow(); 185 | }); 186 | }); 187 | }); 188 | -------------------------------------------------------------------------------- /src/typegoose-core.module.ts: -------------------------------------------------------------------------------- 1 | import * as mongoose from 'mongoose'; 2 | import { models } from '@typegoose/typegoose/lib/internal/data'; 3 | import { DynamicModule, Global, Module, Provider, OnApplicationShutdown, Inject } from '@nestjs/common'; 4 | import { ModuleRef } from '@nestjs/core'; 5 | import { TypegooseOptionsFactory, TypegooseModuleOptions, TypegooseModuleAsyncOptions, TypegooseConnectionOptions } from './typegoose-options.interface'; 6 | import { TYPEGOOSE_CONNECTION_NAME, TYPEGOOSE_MODULE_OPTIONS } from './typegoose.constants'; 7 | import { getConnectionToken } from './typegoose.utils'; 8 | import { deleteModel } from '@typegoose/typegoose'; 9 | 10 | @Global() 11 | @Module({}) 12 | export class TypegooseCoreModule implements OnApplicationShutdown { 13 | constructor( 14 | @Inject(TYPEGOOSE_CONNECTION_NAME) private readonly connectionName: string, 15 | private readonly moduleRef: ModuleRef 16 | ) {} 17 | 18 | /** 19 | * Creates the connection to the mongo database for all the models to use. 20 | * @param uri the uri for the mongoose connection (example: mongodb://mongodb0.example.com:27017/admin). Read more [here](https://docs.mongodb.com/manual/reference/connection-string/). 21 | * @param options the options for the Typegoose connection. You may provide a custom connection name, via `connectionName`, for multiple connections (Read more about [multiple connections here](https://mongoosejs.com/docs/connections.html#options)). Read more about mongoose options [here](https://mongoosejs.com/docs/connections.html#options). 22 | * @internal 23 | */ 24 | static forRoot( 25 | uri: string, 26 | options: TypegooseConnectionOptions = {} 27 | ): DynamicModule { 28 | const connectionName = getConnectionToken(options.connectionName); 29 | 30 | const connectionNameProvider = { 31 | provide: TYPEGOOSE_CONNECTION_NAME, 32 | useValue: connectionName 33 | }; 34 | 35 | const connectionProvider = { 36 | provide: connectionName, 37 | useFactory: () => mongoose.createConnection(uri, options) 38 | }; 39 | 40 | return { 41 | module: TypegooseCoreModule, 42 | providers: [connectionProvider, connectionNameProvider], 43 | exports: [connectionProvider] 44 | }; 45 | } 46 | 47 | /** 48 | * Similar to `forRoot` but is asynchronous instead. Read more [here](https://github.com/kpfromer/nestjs-typegoose#async-mongoose-schema-options). 49 | * @param options the options for the Typegoose connection. You may provide a custom connection name, via `connectionName`, for multiple connections (Read more about [multiple connections here](https://mongoosejs.com/docs/connections.html#options)). Read more about mongoose options [here](https://mongoosejs.com/docs/connections.html#options). 50 | * @internal 51 | */ 52 | static forRootAsync(options: TypegooseModuleAsyncOptions): DynamicModule { 53 | const connectionName = getConnectionToken(options.connectionName); 54 | 55 | const connectionNameProvider = { 56 | provide: TYPEGOOSE_CONNECTION_NAME, 57 | useValue: connectionName 58 | }; 59 | 60 | const connectionProvider = { 61 | provide: connectionName, 62 | useFactory: (typegooseModuleOptions: TypegooseModuleOptions) => { 63 | const { 64 | uri, 65 | ...typegooseOptions 66 | } = typegooseModuleOptions; 67 | return mongoose.createConnection(uri, typegooseOptions); 68 | }, 69 | inject: [TYPEGOOSE_MODULE_OPTIONS] // inject output of async config creator 70 | }; 71 | const asyncProviders = this.createAsyncProviders(options); 72 | return { 73 | module: TypegooseCoreModule, 74 | imports: options.imports, // imports from async for root 75 | providers: [ 76 | ...asyncProviders, 77 | connectionProvider, 78 | connectionNameProvider 79 | ], 80 | exports: [connectionProvider] 81 | }; 82 | } 83 | 84 | /** 85 | * Creates the asynchronous providers handling the creation of the connection options needed for the providers. 86 | * @param options the provider options and connection name needed to create the asynchronous provider. 87 | * @internal 88 | */ 89 | private static createAsyncProviders(options: TypegooseModuleAsyncOptions): Provider[] { 90 | if (options.useExisting || options.useFactory) { 91 | return [this.createAsyncOptionsProvider(options)]; 92 | } 93 | return [ 94 | this.createAsyncOptionsProvider(options), 95 | { 96 | provide: options.useClass, 97 | useClass: options.useClass, 98 | }, 99 | ]; 100 | } 101 | 102 | /** 103 | * Creates the typegoose connection options provider. 104 | * @param options the provider options wrapping the typegoose connection options. 105 | * @internal 106 | */ 107 | private static createAsyncOptionsProvider(options: TypegooseModuleAsyncOptions): Provider { 108 | if (options.useFactory) { // If a factory provider 109 | return { 110 | provide: TYPEGOOSE_MODULE_OPTIONS, 111 | useFactory: options.useFactory, 112 | inject: options.inject || [], 113 | }; 114 | } // else MongooseOptionsFactory 115 | return { 116 | provide: TYPEGOOSE_MODULE_OPTIONS, 117 | useFactory: async (optionsFactory: TypegooseOptionsFactory) => 118 | await optionsFactory.createTypegooseOptions(), 119 | inject: [options.useExisting || options.useClass], 120 | }; 121 | } 122 | 123 | /** 124 | * Cleans up the connection and removes the models to prevent unintended usage. 125 | * @internal 126 | */ 127 | async onApplicationShutdown() { 128 | const connection = this.moduleRef.get(this.connectionName); 129 | 130 | if (connection) { 131 | await connection.close(); 132 | [...models.entries()].reduce((array, [key, model]) => { 133 | if (model.db === connection) { 134 | array.push(key); 135 | } 136 | return array; 137 | }, []).forEach(deleteModel); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/typegoose-options.interface.ts: -------------------------------------------------------------------------------- 1 | import { Type } from '@nestjs/common'; 2 | import { ModuleMetadata } from '@nestjs/common/interfaces'; 3 | import { ConnectionOptions } from 'mongoose'; 4 | 5 | export interface TypegooseConnectionOptions extends ConnectionOptions { 6 | connectionName?: string; 7 | } 8 | 9 | export interface TypegooseModuleOptions { 10 | uri: string; 11 | [key: string]: any; 12 | } 13 | 14 | export interface TypegooseOptionsFactory { 15 | createTypegooseOptions(): 16 | | Promise 17 | | TypegooseModuleOptions; 18 | } 19 | 20 | export interface TypegooseModuleAsyncOptions extends Pick { 21 | connectionName?: string; 22 | useExisting?: Type; 23 | useClass?: Type; 24 | useFactory?: ( 25 | ...args: any[] 26 | ) => Promise | TypegooseModuleOptions; 27 | inject?: any[]; 28 | } 29 | -------------------------------------------------------------------------------- /src/typegoose.constants.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * The name for the default database connection provider. 3 | * @internal 4 | */ 5 | export const DEFAULT_DB_CONNECTION_NAME = 'DefaultTypegooseConnection'; 6 | 7 | /** 8 | * The provider name for the provider that gives the name of the database name provider. 9 | * @internal 10 | */ 11 | export const TYPEGOOSE_CONNECTION_NAME = 'TypegooseConnectionName'; 12 | 13 | /** 14 | * The provider name for the typegoose module options. 15 | * @internal 16 | */ 17 | export const TYPEGOOSE_MODULE_OPTIONS = 'TypegooseModuleOptions'; 18 | -------------------------------------------------------------------------------- /src/typegoose.decorators.spec.ts: -------------------------------------------------------------------------------- 1 | import { prop } from '@typegoose/typegoose'; 2 | import * as nest from '@nestjs/common'; 3 | import { InjectModel } from './typegoose.decorators'; 4 | 5 | jest.mock('@nestjs/common', () => ({ 6 | Inject: jest.fn() 7 | })); 8 | 9 | class MockUser { 10 | @prop() 11 | name: string; 12 | } 13 | 14 | describe('InjectModel', () => { 15 | it('should inject the model', () => { 16 | InjectModel(MockUser); 17 | 18 | expect(nest.Inject).toHaveBeenCalledWith('MockUserModel'); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /src/typegoose.decorators.ts: -------------------------------------------------------------------------------- 1 | import { Inject } from '@nestjs/common'; 2 | import { TypegooseClass } from './typegoose-class.interface'; 3 | import { getModelToken } from './typegoose.utils'; 4 | 5 | /** 6 | * Used to return the inject the mongoose model. 7 | * @param model - the model class wanted to be injected 8 | * @returns the annotation for injecting model 9 | * @internal 10 | */ 11 | export const InjectModel = (model: TypegooseClass) => 12 | Inject(getModelToken(model.name)); 13 | -------------------------------------------------------------------------------- /src/typegoose.module.spec.ts: -------------------------------------------------------------------------------- 1 | import { prop } from '@typegoose/typegoose'; 2 | import { TypegooseModule } from './typegoose.module'; 3 | import { TypegooseCoreModule as CoreModule } from './typegoose-core.module'; 4 | import * as createProviders from './typegoose.providers'; 5 | 6 | class MockTask { 7 | @prop() 8 | description: string; 9 | } 10 | 11 | class MockUser { 12 | @prop() 13 | name: string; 14 | } 15 | 16 | describe('TypegooseModule', () => { 17 | 18 | describe('forRoot', () => { 19 | it('should call global CoreModule forRoot', () => { 20 | jest.spyOn(CoreModule, 'forRoot').mockImplementation(() => ({ 21 | providers: 'DbConnection' 22 | } as any)); 23 | 24 | const module = TypegooseModule.forRoot('mongourl', {db: 'db settings'}); 25 | 26 | expect(module).toEqual({ 27 | module: TypegooseModule, 28 | imports: [ 29 | { 30 | providers: 'DbConnection' 31 | } 32 | ] 33 | }); 34 | 35 | expect(CoreModule.forRoot).toHaveBeenCalledWith('mongourl', {db: 'db settings'}); 36 | }); 37 | 38 | it('should call global CoreModule forRoot with empty config', () => { 39 | jest.spyOn(CoreModule, 'forRoot').mockImplementation(() => ({ 40 | providers: 'DbConnection' 41 | } as any)); 42 | 43 | TypegooseModule.forRoot('mongourl'); 44 | 45 | expect(CoreModule.forRoot).toHaveBeenCalledWith('mongourl', {}); 46 | }); 47 | }); 48 | 49 | describe('forRootAsync', () => { 50 | it('should call global CoreModule forRoot', () => { 51 | jest.spyOn(CoreModule, 'forRootAsync').mockImplementation(() => ({ 52 | providers: 'DbConnection' 53 | } as any)); 54 | 55 | const options = { 56 | useFactory: () => { 57 | return { 58 | uri: 'mongourl', 59 | db: 'db settings' 60 | }; 61 | } 62 | }; 63 | 64 | const module = TypegooseModule.forRootAsync(options); 65 | 66 | expect(module).toEqual({ 67 | module: TypegooseModule, 68 | imports: [ 69 | { 70 | providers: 'DbConnection' 71 | } 72 | ] 73 | }); 74 | 75 | expect(CoreModule.forRootAsync).toHaveBeenCalledWith(options); 76 | }); 77 | }); 78 | 79 | describe('forFeature', () => { 80 | let models, convertedModels; 81 | beforeEach(() => { 82 | models = [ 83 | MockTask, 84 | { 85 | typegooseClass: MockUser, 86 | schemaOptions: { 87 | collection: 'differentCollectionNameUser' 88 | } 89 | } 90 | ]; 91 | 92 | let count = -1; 93 | convertedModels = [ 94 | 'convertedTask', 95 | 'convertedUser' 96 | ]; 97 | 98 | jest.spyOn(createProviders, 'convertToTypegooseClassWithOptions') 99 | .mockImplementation(() => { 100 | count += 1; 101 | return convertedModels[count]; 102 | }); 103 | 104 | jest.spyOn(createProviders, 'createTypegooseProviders') 105 | .mockReturnValue('createdProviders' as any); 106 | }); 107 | 108 | it('should return module that exports providers for models', () => { 109 | const module = TypegooseModule.forFeature(models); 110 | 111 | const expectedProviders = 'createdProviders'; 112 | 113 | expect(createProviders.convertToTypegooseClassWithOptions).toHaveBeenCalledWith(MockTask); 114 | expect(createProviders.convertToTypegooseClassWithOptions).toHaveBeenCalledWith({ 115 | typegooseClass: MockUser, 116 | schemaOptions: { 117 | collection: 'differentCollectionNameUser' 118 | } 119 | }); 120 | 121 | expect(createProviders.createTypegooseProviders).toHaveBeenCalledWith(undefined, convertedModels); 122 | expect(module).toEqual({ 123 | module: TypegooseModule, 124 | providers: expectedProviders, 125 | exports: expectedProviders 126 | }); 127 | }); 128 | 129 | it('should return module that createdTypegooseProviders with provided connectionName', () => { 130 | const connectionName = 'OtherMongoDB'; 131 | 132 | TypegooseModule.forFeature(models, connectionName); 133 | 134 | expect(createProviders.createTypegooseProviders).toHaveBeenCalledWith(connectionName, convertedModels); 135 | }); 136 | }); 137 | }); 138 | -------------------------------------------------------------------------------- /src/typegoose.module.ts: -------------------------------------------------------------------------------- 1 | import { DynamicModule, Module } from '@nestjs/common'; 2 | import { TypegooseCoreModule } from './typegoose-core.module'; 3 | import { createTypegooseProviders, convertToTypegooseClassWithOptions } from './typegoose.providers'; 4 | import { TypegooseClass, TypegooseClassWithOptions } from './typegoose-class.interface'; 5 | import { TypegooseConnectionOptions, TypegooseModuleAsyncOptions } from './typegoose-options.interface'; 6 | 7 | @Module({}) 8 | export class TypegooseModule { 9 | /** 10 | * Creates the connection to the mongo database for all the models to use. 11 | * @param uri the uri for the mongoose connection (example: mongodb://mongodb0.example.com:27017/admin). Read more [here](https://docs.mongodb.com/manual/reference/connection-string/). 12 | * @param options the options for the Typegoose connection. You may provide a custom connection name, via `connectionName`, for multiple connections (Read more about [multiple connections here](https://mongoosejs.com/docs/connections.html#options)). Read more about mongoose options [here](https://mongoosejs.com/docs/connections.html#options). 13 | */ 14 | static forRoot( 15 | uri: string, 16 | options: TypegooseConnectionOptions = {}, 17 | ): DynamicModule { 18 | return { 19 | module: TypegooseModule, 20 | imports: [TypegooseCoreModule.forRoot(uri, options)], 21 | }; 22 | } 23 | 24 | /** 25 | * Similar to `forRoot` but is asynchronous instead. Read more [here](https://github.com/kpfromer/nestjs-typegoose#async-mongoose-schema-options). 26 | * @param options the options for the Typegoose connection. You may provide a custom connection name, via `connectionName`, for multiple connections (Read more about [multiple connections here](https://mongoosejs.com/docs/connections.html#options)). Read more about mongoose options [here](https://mongoosejs.com/docs/connections.html#options). 27 | */ 28 | static forRootAsync(options: TypegooseModuleAsyncOptions): DynamicModule { 29 | return { 30 | module: TypegooseModule, 31 | imports: [TypegooseCoreModule.forRootAsync(options)], 32 | }; 33 | } 34 | 35 | /** 36 | * Provides models for injection into services. 37 | * @param models the list of models to provide to services. 38 | * @param connectionName the connection name for use with multiple connections. 39 | */ 40 | static forFeature(models: (TypegooseClass | TypegooseClassWithOptions)[], connectionName?: string): DynamicModule { 41 | const convertedModels = models.map(model => convertToTypegooseClassWithOptions(model)); 42 | const providers = createTypegooseProviders(connectionName, convertedModels); 43 | return { 44 | module: TypegooseModule, 45 | providers, 46 | exports: providers 47 | }; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/typegoose.providers.spec.ts: -------------------------------------------------------------------------------- 1 | import * as typegoose from '@typegoose/typegoose'; 2 | import { prop, Ref, ReturnModelType } from '@typegoose/typegoose'; 3 | import * as mongoose from 'mongoose'; 4 | import { Connection } from 'mongoose'; 5 | import { MongoMemoryServer } from 'mongodb-memory-server'; 6 | import { DEFAULT_DB_CONNECTION_NAME } from './typegoose.constants'; 7 | import { 8 | createTypegooseProviders, 9 | convertToTypegooseClassWithOptions 10 | } from './typegoose.providers'; 11 | import any = jasmine.any; 12 | 13 | const mongod = new MongoMemoryServer(); 14 | 15 | class MockUser { 16 | @prop() 17 | name: string; 18 | } 19 | 20 | class MockSpecialUser extends MockUser { 21 | @prop() 22 | special: boolean; 23 | } 24 | 25 | class MockExtraSpecialUser extends MockSpecialUser { 26 | @prop() 27 | otherUser: Ref; 28 | } 29 | 30 | class MockTask { 31 | @prop() 32 | description: string; 33 | } 34 | 35 | describe('createTypegooseProviders', () => { 36 | let connection: Connection; 37 | 38 | beforeAll(async () => { 39 | jest.setTimeout(120000); 40 | 41 | connection = await mongoose.createConnection( 42 | await mongod.getConnectionString(), 43 | { 44 | useCreateIndex: true, 45 | useFindAndModify: true, 46 | useNewUrlParser: true, 47 | useUnifiedTopology: true 48 | } 49 | ); 50 | }); 51 | 52 | afterAll(async () => { 53 | await mongoose.connection.close(); 54 | await mongod.stop(); 55 | }); 56 | 57 | describe('setModelForClass', () => { 58 | let mockSetModel, 59 | MockTypegooseClass1, 60 | mockConnection, 61 | schemaOptions, 62 | provider; 63 | beforeEach(() => { 64 | mockSetModel = jest 65 | .spyOn(typegoose, 'getModelForClass') 66 | .mockImplementation(() => jest.fn() as ReturnModelType); 67 | MockTypegooseClass1 = jest.fn(); 68 | mockConnection = jest.fn() as any; 69 | 70 | schemaOptions = { 71 | collection: 'newCollectionName' 72 | }; 73 | 74 | const models = [ 75 | { 76 | typegooseClass: MockTypegooseClass1, 77 | schemaOptions 78 | } 79 | ]; 80 | 81 | [provider] = createTypegooseProviders(DEFAULT_DB_CONNECTION_NAME, models); 82 | provider.useFactory(mockConnection); 83 | }); 84 | 85 | afterEach(() => { 86 | jest.restoreAllMocks(); 87 | }); 88 | 89 | it('should setup the database model', () => { 90 | expect(mockSetModel).toHaveBeenCalled(); 91 | }); 92 | 93 | it('should use existing connection from DbConnectionToken', () => { 94 | expect(mockSetModel.mock.calls[0][1]).toEqual( 95 | expect.objectContaining({ 96 | existingConnection: mockConnection 97 | }) 98 | ); 99 | }); 100 | 101 | it('should forward schema options to typegoose', () => { 102 | expect(mockSetModel.mock.calls[0][1]).toEqual( 103 | expect.objectContaining({ 104 | schemaOptions 105 | }) 106 | ); 107 | }); 108 | }); 109 | 110 | it('should create typegoose providers from models', () => { 111 | jest.setTimeout(30000); 112 | 113 | const models = [ 114 | { 115 | typegooseClass: MockUser 116 | }, 117 | { 118 | typegooseClass: MockTask 119 | } 120 | ]; 121 | 122 | const providers = createTypegooseProviders( 123 | DEFAULT_DB_CONNECTION_NAME, 124 | models 125 | ); 126 | 127 | expect(providers).toEqual([ 128 | { 129 | provide: 'MockUserModel', 130 | useFactory: any(Function), 131 | inject: [DEFAULT_DB_CONNECTION_NAME] 132 | }, 133 | { 134 | provide: 'MockTaskModel', 135 | useFactory: any(Function), 136 | inject: [DEFAULT_DB_CONNECTION_NAME] 137 | } 138 | ]); 139 | 140 | const userProvider = providers[0]; 141 | 142 | const model = userProvider.useFactory(connection); 143 | 144 | expect(model.prototype.model).toBeTruthy(); 145 | }, 15000); 146 | 147 | it('should create typegoose providers from models with discriminators', () => { 148 | jest.setTimeout(30000); 149 | 150 | const customDiscriminatorId = 'extra'; 151 | const models = [ 152 | { 153 | typegooseClass: MockUser, 154 | discriminators: [ 155 | MockSpecialUser, 156 | { 157 | typegooseClass: MockExtraSpecialUser, 158 | discriminatorId: customDiscriminatorId 159 | } 160 | ] 161 | }, 162 | { 163 | typegooseClass: MockTask 164 | } 165 | ]; 166 | 167 | const providers = createTypegooseProviders( 168 | DEFAULT_DB_CONNECTION_NAME, 169 | models 170 | ); 171 | 172 | expect(providers).toEqual([ 173 | { 174 | provide: 'MockUserModel', 175 | useFactory: any(Function), 176 | inject: [DEFAULT_DB_CONNECTION_NAME] 177 | }, 178 | { 179 | provide: 'MockSpecialUserModel', 180 | useFactory: any(Function), 181 | inject: [DEFAULT_DB_CONNECTION_NAME] 182 | }, 183 | { 184 | provide: 'MockExtraSpecialUserModel', 185 | useFactory: any(Function), 186 | inject: [DEFAULT_DB_CONNECTION_NAME] 187 | }, 188 | { 189 | provide: 'MockTaskModel', 190 | useFactory: any(Function), 191 | inject: [DEFAULT_DB_CONNECTION_NAME] 192 | } 193 | ]); 194 | 195 | const specialProvider = providers[1]; 196 | const specialModel = specialProvider.useFactory(connection); 197 | 198 | expect(specialModel.prototype.model).toBeTruthy(); 199 | expect(specialModel).toHaveProperty( 200 | 'schema.discriminatorMapping.value', 201 | MockSpecialUser.name 202 | ); 203 | 204 | const extraProvider = providers[2]; 205 | const extraModel = extraProvider.useFactory(connection); 206 | 207 | expect(extraModel.prototype.model).toBeTruthy(); 208 | expect(extraModel).toHaveProperty( 209 | 'schema.discriminatorMapping.value', 210 | customDiscriminatorId 211 | ); 212 | 213 | const userProvider = providers[0]; 214 | const userModel = userProvider.useFactory(connection); 215 | 216 | expect(userModel.prototype.model).toBeTruthy(); 217 | expect(userModel.discriminators).toHaveProperty( 218 | MockSpecialUser.name, 219 | specialModel 220 | ); 221 | expect(userModel.discriminators).toHaveProperty( 222 | MockExtraSpecialUser.name, 223 | extraModel 224 | ); 225 | }, 15000); 226 | 227 | it('should create no providers if no models are given', () => { 228 | const providers = createTypegooseProviders(DEFAULT_DB_CONNECTION_NAME); 229 | 230 | expect(providers).toEqual([]); 231 | }); 232 | 233 | afterAll(() => { 234 | connection.close(); 235 | }); 236 | }); 237 | 238 | describe('convertToTypegooseClassWithOptions', () => { 239 | class MockTypegooseClass {} 240 | class MockDiscriminator {} 241 | 242 | it('returns model as typegooseClass if it is just a class', () => { 243 | expect(convertToTypegooseClassWithOptions(MockTypegooseClass)).toEqual({ 244 | typegooseClass: MockTypegooseClass 245 | }); 246 | }); 247 | it('returns model and schemaOptions if it is a TypegooseClassWithOptions', () => { 248 | const options = { 249 | collection: 'differentName' 250 | }; 251 | 252 | const expected = { 253 | typegooseClass: MockTypegooseClass, 254 | schemaOptions: options 255 | }; 256 | 257 | expect(convertToTypegooseClassWithOptions(expected)).toEqual(expected); 258 | }); 259 | it('throws error is not a class or not a TypegooseClassWithOptions', () => { 260 | const handler = () => { 261 | expect( 262 | convertToTypegooseClassWithOptions({ 263 | something: 'different' 264 | } as any) 265 | ); 266 | }; 267 | 268 | expect(handler).toThrowErrorMatchingSnapshot(); 269 | }); 270 | 271 | it('returns model with discriminators as typegooseClass if they are just a class', () => { 272 | const options = { 273 | typegooseClass: MockTypegooseClass, 274 | discriminators: [MockDiscriminator] 275 | }; 276 | const expected = { 277 | typegooseClass: MockTypegooseClass, 278 | discriminators: [ 279 | { 280 | typegooseClass: MockDiscriminator 281 | } 282 | ] 283 | }; 284 | expect(convertToTypegooseClassWithOptions(options)).toEqual(expected); 285 | }); 286 | it('returns model with discriminators with options if they are TypegooseDiscriminators', () => { 287 | const expected = { 288 | typegooseClass: MockTypegooseClass, 289 | discriminators: [ 290 | { 291 | typegooseClass: MockDiscriminator, 292 | discriminatorId: 'test' 293 | } 294 | ] 295 | }; 296 | expect(convertToTypegooseClassWithOptions(expected)).toEqual(expected); 297 | }); 298 | it('throws error if a discriminator is not a class or TypegooseDiscriminator', () => { 299 | const handler = () => { 300 | expect( 301 | convertToTypegooseClassWithOptions({ 302 | typegooseClass: MockTypegooseClass, 303 | discriminators: [ 304 | { 305 | something: 'different' 306 | } 307 | ] 308 | } as any) 309 | ); 310 | }; 311 | 312 | expect(handler).toThrowErrorMatchingSnapshot(); 313 | }); 314 | }); 315 | -------------------------------------------------------------------------------- /src/typegoose.providers.ts: -------------------------------------------------------------------------------- 1 | import { FactoryProvider } from '@nestjs/common/interfaces'; 2 | import { getDiscriminatorModelForClass, getModelForClass } from '@typegoose/typegoose'; 3 | import { isClass } from 'is-class'; 4 | import { Connection } from 'mongoose'; 5 | import { TypegooseClass, TypegooseClassWithOptions, TypegooseDiscriminator } from './typegoose-class.interface'; 6 | import { getConnectionToken, getModelToken } from './typegoose.utils'; 7 | 8 | type ModelFactory = (c: Connection) => any; 9 | 10 | /** 11 | * Creates the nestjs providers for the provided models. 12 | * @param connectionName the name of the mongoose connection 13 | * @param models the models to create the nestjs providers 14 | * @returns the model providers. 15 | * @internal 16 | */ 17 | export function createTypegooseProviders(connectionName: string, 18 | models: TypegooseClassWithOptions[] = []): FactoryProvider[] { 19 | 20 | const connectionToken = getConnectionToken(connectionName); 21 | 22 | const buildProvider = ({ name }: TypegooseClass, modelFactory: ModelFactory) => ({ 23 | provide: getModelToken(name), 24 | useFactory: modelFactory, 25 | inject: [connectionToken], 26 | }); 27 | 28 | const createDiscriminatorFactoryFrom = (parentFactory: ModelFactory) => 29 | (discriminatorDefinition: TypegooseClass | TypegooseDiscriminator) => { 30 | if (isTypegooseClass(discriminatorDefinition)) { 31 | return buildProvider(discriminatorDefinition, (connection: Connection) => 32 | getDiscriminatorModelForClass( 33 | parentFactory(connection), 34 | discriminatorDefinition 35 | ) 36 | ); 37 | } 38 | const { typegooseClass, discriminatorId } = discriminatorDefinition; 39 | return buildProvider(typegooseClass, (connection: Connection) => 40 | getDiscriminatorModelForClass( 41 | parentFactory(connection), 42 | typegooseClass, 43 | discriminatorId 44 | ) 45 | ); 46 | }; 47 | 48 | return models.reduce( 49 | (providers, { typegooseClass, schemaOptions = {}, discriminators = [] }) => { 50 | 51 | const modelFactory = (connection: Connection) => getModelForClass( 52 | typegooseClass, 53 | { existingConnection: connection, schemaOptions }, 54 | ); 55 | 56 | const modelProvider = buildProvider(typegooseClass, modelFactory); 57 | 58 | const discriminatorProviders = discriminators.map(createDiscriminatorFactoryFrom(modelFactory)); 59 | 60 | return [...providers, modelProvider, ...discriminatorProviders]; 61 | }, 62 | [], 63 | ); 64 | } 65 | 66 | type ClassOrDiscriminator = TypegooseClassWithOptions | TypegooseDiscriminator; 67 | type TypegooseInput = TypegooseClass | ClassOrDiscriminator; 68 | 69 | /** 70 | * Santizes the input to a common object form used for creating providers. 71 | * @param item varied general input to convert 72 | * @returns a santized generic form 73 | * @internal 74 | */ 75 | export function convertToTypegooseClassWithOptions(item: TypegooseInput): TypegooseClassWithOptions { 76 | const tcwo: TypegooseClassWithOptions = convertToOptions(item); 77 | if (tcwo) { 78 | 79 | if (tcwo.discriminators) { 80 | tcwo.discriminators = tcwo.discriminators.map(d => convertToOptions(d) || invalidObject('discriminator')); 81 | } 82 | 83 | return tcwo; 84 | } 85 | 86 | return invalidObject('model'); 87 | } 88 | 89 | /** 90 | * Returns whether or not something is a class. 91 | * @param item the value to check whether or not it is a class 92 | * @internal 93 | */ 94 | const isTypegooseClass = (item): item is TypegooseClass => isClass(item); 95 | 96 | /** 97 | * Returns whether or not a value is a typegoose class with options. 98 | * @param item the value to check whether or not it is a typegoose class with options 99 | * @internal 100 | */ 101 | const isTypegooseClassWithOptions = (item): item is TypegooseClassWithOptions => isTypegooseClass(item.typegooseClass); 102 | 103 | /** 104 | * Converts a model class to valid typegoose class structure. 105 | * @param item the item to convert 106 | * @returns the valid typegoose class 107 | * @internal 108 | */ 109 | function convertToOptions(item: TypegooseInput): ClassOrDiscriminator | undefined { 110 | if (isTypegooseClass(item)) { 111 | return { typegooseClass: item }; 112 | } else if (isTypegooseClassWithOptions(item)) { 113 | return item; 114 | } 115 | } 116 | 117 | /** 118 | * Throws error representing an invalid provided value. 119 | * @param type the invalid type 120 | * @internal 121 | */ 122 | function invalidObject(type: string): never { 123 | throw new Error(`Invalid ${type} object`); 124 | } 125 | -------------------------------------------------------------------------------- /src/typegoose.utils.ts: -------------------------------------------------------------------------------- 1 | import { DEFAULT_DB_CONNECTION_NAME } from './typegoose.constants'; 2 | 3 | /** 4 | * Returns the provider token name. 5 | * @param model The model name 6 | * @returns The token name 7 | * @internal 8 | */ 9 | export function getModelToken(model: string) { 10 | return `${model}Model`; 11 | } 12 | 13 | /** 14 | * Returns the provider connection token name. 15 | * @param name the name of the connection 16 | * @returns the connection provider name 17 | * @internal 18 | */ 19 | export function getConnectionToken(name?: string) { 20 | if (typeof name === 'string' && name !== DEFAULT_DB_CONNECTION_NAME) { 21 | return `${name}Connection`; 22 | } 23 | return DEFAULT_DB_CONNECTION_NAME; 24 | } 25 | -------------------------------------------------------------------------------- /test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import * as request from 'supertest'; 2 | import { Test } from '@nestjs/testing'; 3 | import { 4 | Body, 5 | Controller, 6 | Module, 7 | Post, 8 | INestApplication 9 | } from '@nestjs/common'; 10 | import { InjectModel, TypegooseModule, getModelToken } from '../src'; 11 | import { prop } from '@typegoose/typegoose'; 12 | import { MongoMemoryServer } from 'mongodb-memory-server'; 13 | 14 | const mongod = new MongoMemoryServer(); 15 | 16 | @Module({ 17 | imports: [TypegooseModule.forRoot('mongoose:uri')] 18 | }) 19 | export class MockApp {} 20 | 21 | class MockTypegooseClass { 22 | @prop() 23 | description; 24 | } 25 | 26 | class MockDiscriminatorParent extends MockTypegooseClass { 27 | @prop() 28 | isParent: boolean; 29 | } 30 | 31 | class MockDiscriminator extends MockDiscriminatorParent { 32 | @prop() 33 | isSubtype: boolean; 34 | } 35 | 36 | @Controller() 37 | class MockController { 38 | constructor(@InjectModel(MockTypegooseClass) private readonly model: any) {} // In reality, it's a Model 39 | 40 | @Post('create') 41 | async createTask(@Body() body: { description: string }) { 42 | return this.model.create({ 43 | description: body.description 44 | }); 45 | } 46 | 47 | @Post('get') 48 | async getTask(@Body() body: { description: string }) { 49 | return this.model.findOne({ 50 | description: body.description 51 | }); 52 | } 53 | } 54 | 55 | @Controller() 56 | class MockSubController { 57 | constructor(@InjectModel(MockDiscriminator) private readonly model: any) {} // In reality, it's a Model 58 | 59 | @Post('createSubTask') 60 | async createSubTask( 61 | @Body() body: { description: string; isSubtype: boolean } 62 | ) { 63 | return this.model.create({ 64 | description: body.description, 65 | isParent: false, 66 | isSubtype: body.isSubtype 67 | }); 68 | } 69 | 70 | @Post('getSubTask') 71 | async getSubTask(@Body() body: { isSubtype: boolean }) { 72 | return this.model.findOne({ 73 | isSubtype: body.isSubtype 74 | }); 75 | } 76 | } 77 | 78 | @Module({ 79 | imports: [ 80 | TypegooseModule.forFeature([ 81 | MockTypegooseClass, 82 | { 83 | typegooseClass: MockDiscriminatorParent, 84 | discriminators: [MockDiscriminator] 85 | } 86 | ]) 87 | ], 88 | controllers: [MockController, MockSubController] 89 | }) 90 | class MockSubModule {} 91 | 92 | describe('App consuming TypegooseModule', () => { 93 | let app; 94 | 95 | beforeAll(async () => { 96 | await mongod.getConnectionString(); 97 | 98 | const moduleFixture = await Test.createTestingModule({ 99 | imports: [MockApp, MockSubModule] 100 | }).compile(); 101 | 102 | app = moduleFixture.createNestApplication(); 103 | await app.init(); 104 | }); 105 | 106 | afterAll(() => mongod.stop()); 107 | 108 | it('should store and get mockTask', async () => { 109 | await request(app.getHttpServer()) 110 | .post('/create') 111 | .send({ 112 | description: 'hello world' 113 | }); 114 | 115 | const response = await request(app.getHttpServer()) 116 | .post('/get') 117 | .send({ 118 | description: 'hello world' 119 | }); 120 | 121 | const body = response.body; 122 | 123 | expect(body._id).toBeTruthy(); 124 | expect(body.description).toBe('hello world'); 125 | }); 126 | }); 127 | 128 | describe('Clear typegoose state after module destroy', () => { 129 | let app: INestApplication; 130 | 131 | beforeAll(async () => { 132 | await mongod.getConnectionString(); 133 | }); 134 | 135 | afterAll(() => mongod.stop()); 136 | 137 | beforeEach(async () => { 138 | const moduleFixture = await Test.createTestingModule({ 139 | imports: [MockApp, MockSubModule] 140 | }).compile(); 141 | 142 | app = moduleFixture.createNestApplication(); 143 | await app.init(); 144 | }); 145 | 146 | afterEach(async () => { 147 | await app.close(); 148 | }); 149 | 150 | Array.from({ length: 2 }).forEach(() => { 151 | it('resolved model should use correct connection', async () => { 152 | const model = await app.get(getModelToken(MockTypegooseClass.name)); 153 | await model.create({ 154 | description: 'test' 155 | }); 156 | }); 157 | }); 158 | 159 | it('should store and get mockSubTask', async () => { 160 | await request(app.getHttpServer()) 161 | .post('/createSubTask') 162 | .send({ 163 | description: 'hello world', 164 | isSubtype: true 165 | }); 166 | 167 | const response = await request(app.getHttpServer()) 168 | .post('/getSubTask') 169 | .send({ 170 | description: 'hello world', 171 | isSubtype: true 172 | }); 173 | 174 | const body = response.body; 175 | 176 | expect(body._id).toBeTruthy(); 177 | expect(body.isParent).toBe(false); 178 | expect(body.isSubtype).toBe(true); 179 | }); 180 | }); 181 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["**/*spec.ts", "dist", "build", "example"], 4 | "compilerOptions": { 5 | "outDir": "./dist" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "noImplicitAny": false, 6 | "strictBindCallApply": true, 7 | "strictFunctionTypes": true, 8 | "removeComments": true, 9 | "emitDecoratorMetadata": true, 10 | "experimentalDecorators": true, 11 | "target": "es2017", 12 | "sourceMap": false, 13 | "outDir": "build", 14 | "baseUrl": "./", 15 | "incremental": true, 16 | "tsBuildInfoFile": ".tsbuildinfo", 17 | "lib": ["esnext"] 18 | }, 19 | "exclude": ["node_modules", "dist", "build", "example"] 20 | } 21 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "warn", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "jsRules": { 7 | "no-unused-expression": true 8 | }, 9 | "rules": { 10 | "eofline": true, 11 | "semicolon": true, 12 | "no-unused-expression": true, 13 | "no-unused-variable": true, 14 | "quotemark": [ 15 | true, 16 | "single" 17 | ], 18 | "indent": [ 19 | true, 20 | "spaces", 21 | 2 22 | ], 23 | "max-line-length": [ 24 | true, 25 | 150 26 | ], 27 | "whitespace": { 28 | "options": [ 29 | "check-branch", 30 | "check-operator", 31 | "check-separator", 32 | "check-preblock", 33 | "check-branch", 34 | "check-module" 35 | ] 36 | }, 37 | "member-access": false, 38 | "ordered-imports": false, 39 | "member-ordering": false, 40 | "curly": false, 41 | "interface-name": false, 42 | "array-type": false, 43 | "no-empty-interface": false, 44 | "no-empty": false, 45 | "arrow-parens": false, 46 | "object-literal-sort-keys": false, 47 | "max-classes-per-file": false, 48 | "variable-name": false, 49 | "one-line": false, 50 | "one-variable-per-declaration": false, 51 | "trailing-comma": false 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /website/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus 2](https://v2.docusaurus.io/), a modern static website generator. 4 | 5 | ### Installation 6 | 7 | ``` 8 | $ yarn 9 | ``` 10 | 11 | ### Local Development 12 | 13 | ``` 14 | $ yarn start 15 | ``` 16 | 17 | This command starts a local development server and open up a browser window. Most changes are reflected live without having to restart the server. 18 | 19 | ### Build 20 | 21 | ``` 22 | $ yarn build 23 | ``` 24 | 25 | This command generates static content into the `build` directory and can be served using any static contents hosting service. -------------------------------------------------------------------------------- /website/docs/async-configuration.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: async-configuration 3 | title: Async Configuration 4 | --- 5 | 6 | To provide asynchronous mongoose schema options (similar to [nestjs mongoose implementation](https://docs.nestjs.com/techniques/mongodb)) you can use the `TypegooseModule.forRootAsync` 7 | 8 | ```typescript 9 | @Module({ 10 | imports: [ 11 | TypegooseModule.forRootAsync({ 12 | imports: [ConfigModule], 13 | useFactory: async (configService: ConfigService) => ({ 14 | uri: configService.getString("MONGODB_URI") 15 | // ...typegooseOptions (Note: config is spread with the uri) 16 | }), 17 | inject: [ConfigService] 18 | }) 19 | ] 20 | }) 21 | export class CatsModule {} 22 | ``` 23 | 24 | #### Note: typegooseOptions with async 25 | 26 | The typegooseOptions is spread with the `uri`. The `uri` is **required**! 27 | 28 | You can also use a class with `useClass` 29 | 30 | ```typescript 31 | import { 32 | TypegooseOptionsFactory, 33 | TypegooseModuleOptions 34 | } from "nestjs-typegoose"; 35 | 36 | class TypegooseConfigService extends TypegooseOptionsFactory { 37 | createTypegooseOptions(): 38 | | Promise 39 | | TypegooseModuleOptions { 40 | return { 41 | uri: "mongodb://localhost/nest" 42 | }; 43 | } 44 | } 45 | 46 | @Module({ 47 | imports: [ 48 | TypegooseModule.forRootAsync({ 49 | useClass: TypegooseConfigService 50 | }) 51 | ] 52 | }) 53 | export class CatsModule {} 54 | ``` 55 | 56 | Or if you want to prevent creating another `TypegooseConfigService` class and want to use it from another imported module then use `useExisting` 57 | 58 | ```typescript 59 | @Module({ 60 | imports: [ 61 | TypegooseModule.forRootAsync({ 62 | imports: [ConfigModule], 63 | useExisting: ConfigService 64 | }) 65 | ] 66 | }) 67 | export class CatsModule {} 68 | ``` -------------------------------------------------------------------------------- /website/docs/discriminators.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: discriminators 3 | title: Mongoose Discriminators 4 | --- 5 | 6 | To add [discriminators](https://mongoosejs.com/docs/discriminators.html) to a model, you may specify a `discriminators` array in the long-form options shown above. 7 | 8 | You may either add just the class, or if you need to override the [discriminator key](https://mongoosejs.com/docs/discriminators.html#discriminator-keys) value, an object with `typegooseClass` and `discriminatorId` property. 9 | 10 | For example: 11 | ```typescript 12 | class Tabby extends Cat { 13 | @prop() 14 | spotted: boolean 15 | } 16 | 17 | class BlackCat extends Cat { 18 | @prop() 19 | unlucky: boolean 20 | } 21 | 22 | @Module({ 23 | imports: [ 24 | TypegooseModule.forFeature([ 25 | { 26 | typegooseClass: Cat, 27 | discriminators: [ 28 | Tabby, 29 | { 30 | typegooseClass: BlackCat, 31 | discriminatorId: 'Black' 32 | } 33 | ] 34 | } 35 | ]) 36 | ] 37 | }) 38 | export class CatsModule {} 39 | ``` 40 | -------------------------------------------------------------------------------- /website/docs/faq.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: faq 3 | title: FAQ 4 | --- 5 | 6 | ## 'useNewUrlParser' does not exist in type 'TypegooseConnectionOptions' 7 | 8 | **A:** Make sure that you have the typings for mongoose installed. `npm install --save-dev @types/mongoose` 9 | -------------------------------------------------------------------------------- /website/docs/install.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: install 3 | title: Installation 4 | --- 5 | 6 | :::note 7 | This assumes you have a nestjs project to work with. If you don't I recommend that you use [nestjs cli](https://docs.nestjs.com/cli/overview) to jump start an example to project to get started using `nestjs-typegoose`. 8 | ::: 9 | 10 | Using `npm`: 11 | 12 | `npm install --save nestjs-typegoose` 13 | 14 | Using `yarn`: 15 | 16 | `yarn add nestjs-typegoose` 17 | 18 | 19 | ## Peer Dependencies 20 | 21 | nestjs-typegoose requires a few peer dependencies to be install for things to work. You need to install the following: 22 | 23 | - `@typegoose/typegoose` version +6.1.5 24 | - `@nestjs/common` +6.10.1 25 | - `@nestjs/core` +6.10.1 26 | - `mongoose` (with the typings `@types/mongoose`) +5.7.12 27 | 28 | Using `npm`: 29 | 30 | `npm install --save @typegoose/typegoose @nestjs/common @nestjs/core mongoose` **and** `npm install --save-dev @types/mongoose` 31 | 32 | Using `yarn`: 33 | 34 | `yarn add @typegoose/typegoose @nestjs/common @nestjs/core mongoose` **and** `yarn add --dev @types/mongoose` 35 | 36 | Now you are ready to get [ready to start](usage.md) using `nestjs-typegoose`! -------------------------------------------------------------------------------- /website/docs/multiple-connections.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: multiple-connections 3 | title: Multiple MongoDB Connections 4 | --- 5 | 6 | To have multiple MongoDB connections one needs to add a `connectionName` string to `forRoot` and `forFeature`. 7 | 8 | ## `forRoot` usage 9 | 10 | **app.module.ts** 11 | 12 | ```typescript 13 | import { Module } from "@nestjs/common"; 14 | import { TypegooseModule } from "nestjs-typegoose"; 15 | 16 | @Module({ 17 | imports: [ 18 | TypegooseModule.forRoot("mongodb://localhost:27017/otherdb", { 19 | useNewUrlParser: true, 20 | connectionName: "other-mongodb" 21 | }), 22 | CatsModule 23 | ] 24 | }) 25 | export class ApplicationModule {} 26 | ``` 27 | 28 | **cat.module.ts** 29 | 30 | ```typescript 31 | @Module({ 32 | imports: [TypegooseModule.forFeature([Cat], "other-mongodb")], 33 | controllers: [CatsController], 34 | providers: [CatsService] 35 | }) 36 | export class CatsModule {} 37 | ``` 38 | 39 | Here the CatsService will use the `other-mongodb` connection defined in the `forRoot`. 40 | 41 | ## `forRootAsync` usage 42 | 43 | Same **cat.module.ts** as above for the `forFeature`. 44 | 45 | **cat.module.ts** 46 | 47 | ```typescript 48 | @Module({ 49 | imports: [TypegooseModule.forFeature([Cat], "other-mongodb")], 50 | controllers: [CatsController], 51 | providers: [CatsService] 52 | }) 53 | export class CatsModule {} 54 | ``` 55 | 56 | And for `forRootAsync` add `connectionName` to the options as well. 57 | 58 | ```typescript 59 | @Module({ 60 | imports: [ 61 | TypegooseModule.forRootAsync({ 62 | connectionName: "other-mongodb", 63 | imports: [ConfigModule], 64 | useFactory: async (configService: ConfigService) => ({ 65 | uri: configService.getString("MONGODB_URI"), 66 | // ...typegooseOptions (Note: config is spread with the uri) 67 | }), 68 | inject: [ConfigService] 69 | }) 70 | ] 71 | }) 72 | export class CatsModule {} -------------------------------------------------------------------------------- /website/docs/schema-options.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: schema-options 3 | title: Schema Options 4 | --- 5 | 6 | To add custom [mongoose schema options](http://mongoosejs.com/docs/guide.html#options) you can simply change `Typegoose.forFeature` to the following format: 7 | 8 | ```typescript 9 | @Module({ 10 | imports: [ 11 | TypegooseModule.forFeature([ 12 | { 13 | typegooseClass: Cat, 14 | schemaOptions: { 15 | collection: "ADifferentCollectionNameForCats" 16 | } 17 | } 18 | ]) 19 | ] 20 | }) 21 | export class CatsModule {} 22 | ``` -------------------------------------------------------------------------------- /website/docs/testing.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: testing 3 | title: Testing 4 | --- 5 | 6 | Like [@nestjs/mongoose](https://docs.nestjs.com/v5/techniques/mongodb) (see the testing section) nestjs-typegoose's `forFeature` and `forRoot` rely on a database connection to work. To unit test your `CatService` without connecting to a mongo database you need to create a fake model using a [custom provider](https://docs.nestjs.com/fundamentals/custom-providers). 7 | 8 | ```typescript 9 | import { getModelToken } from "nestjs-typegoose"; 10 | 11 | @Module({ 12 | ProductService, 13 | { 14 | provide: getModelToken('Product'), 15 | useValue: fakeProductModel 16 | } 17 | }) 18 | ``` 19 | 20 | In a spec file this would look like: 21 | 22 | ```typescript 23 | 24 | const fakeProductModel = jest.fn(); 25 | 26 | const module: TestingModule = await Test.createTestingModule({ 27 | providers: [ 28 | { 29 | provide: getModelToken("Product"), 30 | useValue: fakeProductModel 31 | }, 32 | ProductService 33 | ] 34 | }).compile(); 35 | ``` 36 | 37 | The string given to `getModelToken` function should be the class name of the typegoose model that you are testing. 38 | 39 | -------------------------------------------------------------------------------- /website/docs/usage.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: usage 3 | title: Basic Usage 4 | --- 5 | 6 | :::note 7 | Check out this [example project](https://github.com/kpfromer/nestjs-typegoose/tree/master/example) if you need help. 8 | ::: 9 | 10 | You can check out the `example` project for more details. 11 | 12 | We will be creating a `CatsModule`, a `Cat` database model, a `CatsService`, and a `CatsController`. 13 | 14 | ## Connecting to the MongoDB database 15 | 16 | First, we will connect to the mongo database using `TypegooseModule.forRoot`. We will import the `CatsModule` that we will create shortly. 17 | 18 | If you want to have more connections to different databases read about how to do that [here](multiple-connections.md). 19 | 20 | **app.module.ts** 21 | 22 | ```typescript 23 | import { Module } from "@nestjs/common"; 24 | import { TypegooseModule } from "nestjs-typegoose"; 25 | import { CatsModule } from "./cat.module.ts"; 26 | 27 | @Module({ 28 | imports: [ 29 | TypegooseModule.forRoot("mongodb://localhost:27017/nest", { 30 | useNewUrlParser: true 31 | }), 32 | CatsModule 33 | ] 34 | }) 35 | export class ApplicationModule {} 36 | ``` 37 | 38 | Here we are connecting to `mongodb://localhost:27017/nest`. To learn more about MongoDB URI's see the official [mongodb article](https://docs.mongodb.com/manual/reference/connection-string/). 39 | 40 | ## Creating a Database Model 41 | 42 | We now need to create a database model that describes the data we want to store. In this case, it will be cats with names. Read more about typegoose [here](https://github.com/typegoose/typegoose). 43 | 44 | **cat.model.ts** 45 | 46 | ```typescript 47 | import { prop } from "@typegoose/typegoose"; 48 | 49 | export class Cat { 50 | @prop({ required: true }) 51 | name: string; 52 | } 53 | ``` 54 | 55 | ## Creating the service 56 | 57 | We need to create a service to handle the business logic of creating, reading, updating, and deleting (CRUD) entires, or cats, in the database. 58 | 59 | **cats.service.ts** 60 | 61 | ```typescript 62 | import { Injectable } from "@nestjs/common"; 63 | import { InjectModel } from "nestjs-typegoose"; 64 | import { Cat } from "./cat.model"; 65 | import { ReturnModelType } from "@typegoose/typegoose"; 66 | 67 | @Injectable() 68 | export class CatsService { 69 | constructor( 70 | @InjectModel(Cat) private readonly catModel: ReturnModelType 71 | ) {} 72 | 73 | async create(createCatDto: { name: string }): Promise { 74 | const createdCat = new this.catModel(createCatDto); 75 | return await createdCat.save(); 76 | } 77 | 78 | async findAll(): Promise { 79 | return await this.catModel.find().exec(); 80 | } 81 | } 82 | ``` 83 | 84 | ## Connecting with the API 85 | 86 | Now we have the service created we need to connect this with the actual API calls. The `CatsController` will receive GET and POST requests on the URL `/cats` and will get and create cats respectively. 87 | 88 | **cats.controller.ts** 89 | 90 | ```typescript 91 | import { Controller, Get, Post, Body } from "@nestjs/common"; 92 | import { CatsService } from "./cats.service"; 93 | import { Cat } from "./cats.model.ts"; 94 | 95 | @Controller("cats") 96 | export class CatsController { 97 | constructor(private readonly catsService: CatsService) {} 98 | 99 | @Get() 100 | async getCats(): Promise { 101 | return await this.catsService.findAll(); 102 | } 103 | 104 | @Post() 105 | async create(@Body() cat: Cat): Promise { 106 | return await this.catsService.create(cat); 107 | } 108 | } 109 | ``` 110 | 111 | ## Providing the model for our services 112 | 113 | We have to make sure we provide the needed models to our service with `TypegooseModule.forFeature` for the `InjectModel` to work. This helps prevents unauthorized access to other models. 114 | 115 | **cat.module.ts** 116 | 117 | ```typescript 118 | import { Module } from "@nestjs/common"; 119 | import { TypegooseModule } from "nestjs-typegoose"; 120 | import { Cat } from "./cat.model"; 121 | import { CatsController } from "./cats.controller"; 122 | import { CatsService } from "./cats.service"; 123 | 124 | @Module({ 125 | imports: [TypegooseModule.forFeature([Cat])], 126 | controllers: [CatsController], 127 | providers: [CatsService] 128 | }) 129 | export class CatsModule {} 130 | ``` 131 | 132 | That's it, you have created a simple working api with `nestjs-typegoose`! -------------------------------------------------------------------------------- /website/docusaurus.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | title: "nestjs-typegoose", 3 | tagline: "Combine NestJS with Typegoose to reduce clutter", 4 | url: "https://kpfromer.github.io", 5 | baseUrl: "/nestjs-typegoose/", 6 | favicon: "img/favicon.ico", 7 | organizationName: "kpfromer", 8 | projectName: "nestjs-typegoose", 9 | themeConfig: { 10 | navbar: { 11 | title: "Nestjs Typegoose", 12 | // logo: { 13 | // alt: 'My Site Logo', 14 | // src: 'img/logo.svg', 15 | // }, 16 | items: [ 17 | { to: "docs/install", label: "Docs", position: "left" }, 18 | { 19 | href: "https://github.com/kpfromer/nestjs-typegoose", 20 | label: "GitHub", 21 | position: "right", 22 | }, 23 | ], 24 | }, 25 | footer: { 26 | style: "dark", 27 | links: [ 28 | { 29 | title: "Docs", 30 | items: [ 31 | { 32 | label: "Installation", 33 | to: "docs/install", 34 | }, 35 | { 36 | label: "Getting Started", 37 | to: "docs/usage", 38 | }, 39 | ], 40 | }, 41 | { 42 | title: "Social", 43 | items: [ 44 | { 45 | label: "GitHub", 46 | href: "https://github.com/kpfromer/nestjs-typegoose", 47 | }, 48 | ], 49 | }, 50 | ], 51 | copyright: `Copyright © ${new Date().getFullYear()} Kyle Pfromer.`, 52 | }, 53 | }, 54 | presets: [ 55 | [ 56 | "@docusaurus/preset-classic", 57 | { 58 | docs: { 59 | sidebarPath: require.resolve("./sidebars.js"), 60 | editUrl: 61 | "https://github.com/kpfromer/nestjs-typegoose/edit/master/website/", 62 | }, 63 | theme: { 64 | customCss: require.resolve("./src/css/custom.css"), 65 | }, 66 | }, 67 | ], 68 | ], 69 | }; 70 | -------------------------------------------------------------------------------- /website/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "website", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "docusaurus start", 7 | "build": "docusaurus build", 8 | "swizzle": "docusaurus swizzle", 9 | "deploy": "docusaurus deploy" 10 | }, 11 | "dependencies": { 12 | "@docusaurus/core": "2.0.0-alpha.63", 13 | "@docusaurus/preset-classic": "2.0.0-alpha.63", 14 | "classnames": "2.2.6", 15 | "react": "16.13.1", 16 | "react-dom": "16.13.1" 17 | }, 18 | "browserslist": { 19 | "production": [ 20 | ">0.2%", 21 | "not dead", 22 | "not op_mini all" 23 | ], 24 | "development": [ 25 | "last 1 chrome version", 26 | "last 1 firefox version", 27 | "last 1 safari version" 28 | ] 29 | } 30 | } -------------------------------------------------------------------------------- /website/sidebars.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2017-present, Facebook, Inc. 3 | * 4 | * This source code is licensed under the MIT license found in the 5 | * LICENSE file in the root directory of this source tree. 6 | */ 7 | 8 | module.exports = { 9 | someSidebar: { 10 | Basics: ['install', 'usage', 'testing', 'faq'], 11 | Advanced: ['discriminators', 'multiple-connections', 'async-configuration', 'schema-options'] 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /website/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #3578e5; 10 | --ifm-color-primary-dark: #1d68e1; 11 | --ifm-color-primary-darker: #1b62d4; 12 | --ifm-color-primary-darkest: #1751af; 13 | --ifm-color-primary-light: #4e89e8; 14 | --ifm-color-primary-lighter: #5a91ea; 15 | --ifm-color-primary-lightest: #80aaef; 16 | --ifm-code-font-size: 95%; 17 | } 18 | 19 | .docusaurus-highlight-code-line { 20 | background-color: rgb(72, 77, 91); 21 | display: block; 22 | margin: 0 calc(-1 * var(--ifm-pre-padding)); 23 | padding: 0 var(--ifm-pre-padding); 24 | } 25 | -------------------------------------------------------------------------------- /website/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames'; 3 | import Layout from '@theme/Layout'; 4 | import Link from '@docusaurus/Link'; 5 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 6 | import useBaseUrl from '@docusaurus/useBaseUrl'; 7 | import styles from './styles.module.css'; 8 | 9 | // const features = [ 10 | // { 11 | // title: <>Easy to Use, 12 | // imageUrl: 'img/undraw_docusaurus_mountain.svg', 13 | // description: ( 14 | // <> 15 | // Docusaurus was designed from the ground up to be easily installed and 16 | // used to get your website up and running quickly. 17 | // 18 | // ), 19 | // }, 20 | // { 21 | // title: <>Focus on What Matters, 22 | // imageUrl: 'img/undraw_docusaurus_tree.svg', 23 | // description: ( 24 | // <> 25 | // Docusaurus lets you focus on your docs, and we'll do the chores. Go 26 | // ahead and move your docs into the docs directory. 27 | // 28 | // ), 29 | // }, 30 | // { 31 | // title: <>Powered by React, 32 | // imageUrl: 'img/undraw_docusaurus_react.svg', 33 | // description: ( 34 | // <> 35 | // Extend or customize your website layout by reusing React. Docusaurus can 36 | // be extended while reusing the same header and footer. 37 | // 38 | // ), 39 | // }, 40 | // ]; 41 | 42 | function Feature({imageUrl, title, description}) { 43 | const imgUrl = useBaseUrl(imageUrl); 44 | return ( 45 |
46 | {imgUrl && ( 47 |
48 | {title} 49 |
50 | )} 51 |

{title}

52 |

{description}

53 |
54 | ); 55 | } 56 | 57 | function Home() { 58 | const context = useDocusaurusContext(); 59 | const {siteConfig = {}} = context; 60 | return ( 61 | 64 |
65 |
66 |

{siteConfig.title}

67 |

{siteConfig.tagline}

68 |
69 | 75 | Get Started 76 | 77 |
78 |
79 |
80 | {/*
81 | {features && features.length && ( 82 |
83 |
84 |
85 | {features.map((props, idx) => ( 86 | 87 | ))} 88 |
89 |
90 |
91 | )} 92 |
*/} 93 |
94 | ); 95 | } 96 | 97 | export default Home; 98 | -------------------------------------------------------------------------------- /website/src/pages/styles.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 966px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | 25 | .features { 26 | display: flex; 27 | align-items: center; 28 | padding: 2rem 0; 29 | width: 100%; 30 | } 31 | 32 | .featureImage { 33 | height: 200px; 34 | width: 200px; 35 | } 36 | -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kpfromer/nestjs-typegoose/de3b8690778967152155503baa5f226808d84cb5/website/static/img/favicon.ico --------------------------------------------------------------------------------