├── .bazelignore ├── .bazelrc ├── .dockerignore ├── .editorconfig ├── .env ├── .gitignore ├── .prettierignore ├── .prettierrc ├── BUILD.bazel ├── README.md ├── WORKSPACE ├── angular-metadata.tsconfig.json ├── angular.json ├── apps ├── api │ ├── jest.config.js │ ├── src │ │ ├── BUILD.bazel │ │ ├── app │ │ │ ├── BUILD.bazel │ │ │ ├── app.controller.spec.ts │ │ │ ├── app.controller.ts │ │ │ ├── app.module.ts │ │ │ ├── app.service.spec.ts │ │ │ ├── app.service.ts │ │ │ └── db.config.ts │ │ ├── auth │ │ │ ├── BUILD.bazel │ │ │ ├── auth-config.module.ts │ │ │ ├── auth.controller.ts │ │ │ ├── auth.module.ts │ │ │ ├── auth.service.ts │ │ │ ├── interfaces │ │ │ │ └── jwt-payload.interface.ts │ │ │ └── jwt.strategy.ts │ │ ├── cats │ │ │ ├── BUILD.bazel │ │ │ ├── cats.controller.ts │ │ │ ├── cats.module.ts │ │ │ ├── cats.service.ts │ │ │ ├── dto │ │ │ │ └── create-cat.dto.ts │ │ │ └── entities │ │ │ │ └── cat.ts │ │ ├── main.ts │ │ └── user │ │ │ ├── BUILD.bazel │ │ │ ├── dto │ │ │ ├── sign-in.dto.ts │ │ │ └── sign-up.dto.ts │ │ │ ├── entities │ │ │ └── user.entity.ts │ │ │ ├── user.module.ts │ │ │ └── user.service.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── tslint.json └── lazy-loading-app │ ├── BUILD.bazel │ ├── browserslist │ ├── jest.config.js │ ├── src │ ├── BUILD.bazel │ ├── app │ │ ├── BUILD.bazel │ │ ├── alert │ │ │ ├── BUILD.bazel │ │ │ ├── alert.component.html │ │ │ ├── alert.component.ts │ │ │ ├── alert.module.ts │ │ │ └── alert.service.ts │ │ ├── app-routing.module.ts │ │ ├── app.component.html │ │ ├── app.component.spec.ts │ │ ├── app.component.ts │ │ ├── app.module.ts │ │ ├── auth │ │ │ ├── BUILD.bazel │ │ │ ├── auth.guard.ts │ │ │ ├── auth.module.ts │ │ │ ├── authentication.service.ts │ │ │ └── index.ts │ │ ├── helpers │ │ │ ├── error.interceptor.ts │ │ │ └── jwt.interceptor.ts │ │ ├── home │ │ │ ├── BUILD.bazel │ │ │ ├── cats.service.ts │ │ │ ├── home.component.html │ │ │ ├── home.component.ts │ │ │ └── home.module.ts │ │ ├── login │ │ │ ├── BUILD.bazel │ │ │ ├── login.component.html │ │ │ ├── login.component.ts │ │ │ └── login.module.ts │ │ └── register │ │ │ ├── BUILD.bazel │ │ │ ├── register.component.html │ │ │ ├── register.component.ts │ │ │ └── register.module.ts │ ├── environments │ │ ├── environment.prod.ts │ │ └── environment.ts │ ├── favicon.ico │ ├── index.html │ ├── initialize_testbed.ts │ ├── main.dev.ts │ ├── main.prod.ts │ ├── main.ts │ ├── polyfills.ts │ ├── rxjs_shims.js │ ├── styles.scss │ └── test-setup.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── tslint.json ├── client.dockerfile ├── docker-compose.yml ├── graph.png ├── jest.config.js ├── libs └── api-interface │ ├── README.md │ ├── jest.config.js │ ├── src │ ├── BUILD.bazel │ ├── index.ts │ └── interfaces │ │ ├── cat.ts │ │ ├── message.ts │ │ └── user.ts │ ├── tsconfig.json │ ├── tsconfig.lib.json │ ├── tsconfig.spec.json │ └── tslint.json ├── nx.json ├── package.json ├── server.dockerfile ├── tsconfig.json ├── tslint.json └── yarn.lock /.bazelignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | -------------------------------------------------------------------------------- /.bazelrc: -------------------------------------------------------------------------------- 1 | #@IgnoreInspection BashAddShebang 2 | 3 | # Common Bazel settings for JavaScript/NodeJS workspaces 4 | # This rc file is automatically discovered when Bazel is run in this workspace, 5 | # see https://docs.bazel.build/versions/master/guide.html#bazelrc 6 | # 7 | # The full list of Bazel options: https://docs.bazel.build/versions/master/command-line-reference.html 8 | 9 | # Make TypeScript and Angular compilation fast, by keeping a few copies of the 10 | # compiler running as daemons, and cache SourceFile AST's to reduce parse time. 11 | build --strategy=TypeScriptCompile=worker 12 | build --strategy=AngularTemplateCompile=worker 13 | 14 | # Bazel will create symlinks from the workspace directory to output artifacts. 15 | # Build results will be placed in a directory called "dist/bin" 16 | # Other directories will be created like "dist/testlogs" 17 | # Be aware that this will still create a bazel-out symlink in 18 | # your project directory, which you must exclude from version control and your 19 | # editor's search path. 20 | build --symlink_prefix=dist/ 21 | 22 | test --test_output=errors 23 | 24 | # Use the Angular 6 compiler 25 | build --define=compile=legacy 26 | 27 | # Turn off legacy external runfiles 28 | # This prevents accidentally depending on this feature, which Bazel will remove. 29 | run --nolegacy_external_runfiles 30 | test --nolegacy_external_runfiles 31 | 32 | # Support for debugging NodeJS tests 33 | # Add the Bazel option `--config=debug` to enable this 34 | test:debug --test_arg=--node_options=--inspect-brk --test_output=streamed --test_strategy=exclusive --test_timeout=9999 --nocache_test_results 35 | 36 | # Turn on --incompatible_strict_action_env which was on by default 37 | # in Bazel 0.21.0 but turned off again in 0.22.0. Follow 38 | # https://github.com/bazelbuild/bazel/issues/7026 for more details. 39 | # This flag is needed to so that the bazel cache is not invalidated 40 | # when running bazel via `yarn bazel`. 41 | # See https://github.com/angular/angular/issues/27514. 42 | build --incompatible_strict_action_env 43 | run --incompatible_strict_action_env 44 | test --incompatible_strict_action_env 45 | 46 | # Turn on the "Managed Directories" feature. 47 | # This allows Bazel to share the same node_modules directory with other tools 48 | # NB: this option was introduced in Bazel 0.26 49 | # See https://docs.bazel.build/versions/master/command-line-reference.html#flag--experimental_allow_incremental_repository_updates 50 | # https://github.com/bazelbuild/rules_nodejs/wiki#migrating-to-rules_nodejs-030 51 | build --experimental_allow_incremental_repository_updates 52 | query --experimental_allow_incremental_repository_updates 53 | 54 | build --incompatible_bzl_disallow_load_after_statement=false 55 | 56 | test --test_output=errors 57 | 58 | # Load any settings specific to the current user. 59 | # .bazelrc.user should appear in .gitignore so that settings are not shared with team members 60 | # This needs to be last statement in this 61 | # config, as the user configuration should be able to overwrite flags from this file. 62 | # See https://docs.bazel.build/versions/master/best-practices.html#bazelrc 63 | # (Note that we use .bazelrc.user so the file appears next to .bazelrc in directory listing, 64 | # rather than user.bazelrc as suggested in the Bazel docs) 65 | try-import %workspace%/.bazelrc.user 66 | 67 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | Dockerfile* 4 | docker-compose* 5 | .dockerignore 6 | .git 7 | .gitignore 8 | README.md 9 | LICENSE 10 | .vscode 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.html] 12 | indent_size = 4 13 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | DB_HOST=localhost 2 | DB_PORT=5432 3 | DB_USERNAME=postgres 4 | DB_PASSWORD=postgres 5 | DB_NAME=bazel-nx-example 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /bazel-out 5 | /dist 6 | /tmp 7 | /out-tsc 8 | 9 | # dependencies 10 | /node_modules 11 | 12 | # IDEs and editors 13 | /.vscode 14 | /.idea 15 | .project 16 | .classpath 17 | .c9/ 18 | *.launch 19 | .settings/ 20 | *.sublime-workspace 21 | 22 | # misc 23 | /.sass-cache 24 | /connect.lock 25 | /coverage 26 | /libpeerconnection.log 27 | npm-debug.log 28 | yarn-error.log 29 | testem.log 30 | /typings 31 | 32 | # System Files 33 | .DS_Store 34 | Thumbs.db 35 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Add files here to ignore them from prettier formatting 2 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /BUILD.bazel: -------------------------------------------------------------------------------- 1 | # Add rules here to build your software 2 | # See https://docs.bazel.build/versions/master/build-ref.html#BUILD_files 3 | 4 | # Allow any ts_library rules in this workspace to reference the config 5 | # Note: if you move the tsconfig.json file to a subdirectory, you can add an alias() here instead 6 | # so that ts_library rules still use it by default. 7 | # See https://www.npmjs.com/package/@bazel/typescript#installation 8 | package(default_visibility = ["//:__subpackages__"]) 9 | 10 | # Allow any ts_library rules in this workspace to reference the config 11 | exports_files(["tsconfig.json"]) 12 | 13 | filegroup( 14 | name = "env_file", 15 | srcs = glob([".env"]), 16 | ) 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Bazel at Scalio](https://raw.githubusercontent.com/scalio/bazel/master/assets/scalio-bnx.svg?sanitize=true) 2 | 3 |

Bazel Nx-Monorepo Starter

4 | 5 |

6 | A full-stack starter app written in Typescript using Nx, Angular, and NestJS -- built using Bazel 7 |

8 | 9 |   10 | ## Overview 11 | This project was created to demonstrate a fully fledged 12 | full-stack typescript application built with Bazel. 13 | It showcases basic CRUD operations, authentication, routing, code-sharing between frontend and backend and more. 14 | 15 | Frontend application was built according to [Angular 8 JWT Authentication Example](https://github.com/cornflourblue/angular-8-jwt-authentication-example). 16 | 17 | The full list of cool technologies used in this project: 18 | - **Lazy loading** of each module in the **Angular** app. 19 | - **Monorepo** architecture using **Nx** that allows TypeScript 20 | interfaces to be shared between frontend and backend. 21 | - [libs/api-interface](libs/api-interface/src/interfaces): 22 | here you can find the shared interfaces. 23 | - **Swagger** module for documentation. 24 | - After the build you can see the documentation for REST API at `http://localhost:3000/api`. 25 | - **Passport** for login using **JWT**. 26 | - To access protected routes you should use the HTTP Header: `Authentication: 'Bearer '` 27 | - **TypeORM + Postgres** for persisting data. 28 | - **Docker-compose** used to run database, frontend and the backend. 29 | - **Bazel** build tool for faster builds 30 | 31 | ## Build & Run 32 | The easiest way to run the API, DB and frontend all together is docker-compose: 33 | ```bash 34 | docker-compose up --build 35 | ``` 36 | 37 | Without docker, things are a little more complicated: 38 | ```bash 39 | # installation 40 | yarn install -D 41 | 42 | # run frontend 43 | yarn start 44 | 45 | # run backend 46 | # assuming DB is already running 47 | yarn start api 48 | ``` 49 | 50 | After the build the app should be available at `http://localhost:4200/` 51 | and API docs are at `http://localhost:3000/api`. 52 | 53 | ## Credits 54 | 55 | Created by [@rayman1104](https://github.com/rayman1104/) @ [Scalio](https://scal.io/) 56 | 57 | 58 | ## About us 59 |

60 |
61 | 62 | 63 | 64 |
65 |

66 | -------------------------------------------------------------------------------- /WORKSPACE: -------------------------------------------------------------------------------- 1 | # The WORKSPACE file tells Bazel that this directory is a "workspace", which is like a project root. 2 | # The content of this file specifies all the external dependencies Bazel needs to perform a build. 3 | 4 | #################################### 5 | # ESModule imports (and TypeScript imports) can be absolute starting with the workspace name. 6 | # The name of the workspace should match the npm package where we publish, so that these 7 | # imports also make sense when referencing the published package. 8 | workspace( 9 | name = "nx_bazel_example", 10 | managed_directories = {"@npm": ["node_modules"]}, 11 | ) 12 | 13 | # These rules are built-into Bazel but we need to load them first to download more rules 14 | load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") 15 | 16 | # Fetch rules_nodejs so we can install our npm dependencies 17 | http_archive( 18 | name = "build_bazel_rules_nodejs", 19 | sha256 = "6625259f9f77ef90d795d20df1d0385d9b3ce63b6619325f702b6358abb4ab33", 20 | urls = ["https://github.com/bazelbuild/rules_nodejs/releases/download/0.35.0/rules_nodejs-0.35.0.tar.gz"], 21 | ) 22 | 23 | http_archive( 24 | name = "io_bazel_rules_sass", 25 | sha256 = "4f05239080175a3f4efa8982d2b7775892d656bb47e8cf56914d5f9441fb5ea6", 26 | strip_prefix = "rules_sass-86ca977cf2a8ed481859f83a286e164d07335116", 27 | url = "https://github.com/bazelbuild/rules_sass/archive/86ca977cf2a8ed481859f83a286e164d07335116.zip", 28 | ) 29 | 30 | # Setup the Node.js toolchain 31 | load("@build_bazel_rules_nodejs//:defs.bzl", "yarn_install") 32 | 33 | # Setup the Node.js toolchain & install our npm dependencies into @npm 34 | yarn_install( 35 | name = "npm", 36 | package_json = "//:package.json", 37 | yarn_lock = "//:yarn.lock", 38 | ) 39 | 40 | # Install any Bazel rules which were extracted earlier by the yarn_install rule. 41 | load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") 42 | 43 | install_bazel_dependencies() 44 | 45 | load("@npm_bazel_karma//:package.bzl", "rules_karma_dependencies") 46 | 47 | rules_karma_dependencies() 48 | 49 | load("@io_bazel_rules_webtesting//web:repositories.bzl", "web_test_repositories") 50 | 51 | web_test_repositories() 52 | 53 | load("@npm_bazel_karma//:browser_repositories.bzl", "browser_repositories") 54 | 55 | browser_repositories() 56 | 57 | load("@npm_bazel_typescript//:index.bzl", "ts_setup_workspace") 58 | 59 | ts_setup_workspace() 60 | 61 | load("@io_bazel_rules_sass//sass:sass_repositories.bzl", "sass_repositories") 62 | 63 | sass_repositories() 64 | 65 | # Download the rules_docker repository 66 | http_archive( 67 | name = "io_bazel_rules_docker", 68 | sha256 = "87fc6a2b128147a0a3039a2fd0b53cc1f2ed5adb8716f50756544a572999ae9a", 69 | strip_prefix = "rules_docker-0.8.1", 70 | urls = ["https://github.com/bazelbuild/rules_docker/archive/v0.8.1.tar.gz"], 71 | ) 72 | 73 | load( 74 | "@io_bazel_rules_docker//repositories:repositories.bzl", 75 | container_repositories = "repositories", 76 | ) 77 | 78 | container_repositories() 79 | 80 | # Download base image for nodejs 81 | load( 82 | "@io_bazel_rules_docker//nodejs:image.bzl", 83 | _nodejs_image_repos = "repositories", 84 | ) 85 | 86 | _nodejs_image_repos() 87 | -------------------------------------------------------------------------------- /angular-metadata.tsconfig.json: -------------------------------------------------------------------------------- 1 | // Workaround for https://github.com/angular/angular/issues/18810 2 | // 3 | // This file is required to run ngc on 3rd party libraries such as @ngrx, 4 | // to write files like node_modules/@ngrx/store/store.ngsummary.json. 5 | { 6 | "compilerOptions": { 7 | "lib": [ 8 | "dom", 9 | "es2015" 10 | ], 11 | "experimentalDecorators": true, 12 | "types": [], 13 | "module": "amd", 14 | "moduleResolution": "node" 15 | }, 16 | "angularCompilerOptions": { 17 | "enableSummariesForJit": true 18 | }, 19 | "include": [ 20 | "node_modules/@angular/**/*" 21 | ], 22 | "exclude": [ 23 | "node_modules/@angular/common/upgrade*", 24 | "node_modules/@angular/router/upgrade*", 25 | "node_modules/@angular/bazel/**", 26 | "node_modules/@angular/compiler-cli/**", 27 | "node_modules/@angular/**/testing/**" 28 | ] 29 | } 30 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "", 5 | "projects": { 6 | "lazy-loading-app": { 7 | "root": "apps/lazy-loading-app/", 8 | "sourceRoot": "apps/lazy-loading-app/src", 9 | "projectType": "application", 10 | "prefix": "lazy", 11 | "schematics": { 12 | "@nrwl/schematics:component": { 13 | "style": "scss" 14 | } 15 | }, 16 | "architect": { 17 | "build": { 18 | "builder": "@angular/bazel:build", 19 | "options": { 20 | "targetLabel": "//apps/lazy-loading-app/src:prodapp", 21 | "bazelCommand": "build" 22 | }, 23 | "configurations": { 24 | "production": { 25 | "targetLabel": "//apps/lazy-loading-app/src:prodapp" 26 | } 27 | } 28 | }, 29 | "serve": { 30 | "builder": "@angular/bazel:build", 31 | "options": { 32 | "targetLabel": "//apps/lazy-loading-app/src:devserver", 33 | "bazelCommand": "run", 34 | "watch": true 35 | }, 36 | "configurations": { 37 | "production": { 38 | "targetLabel": "//apps/lazy-loading-app/src:prodserver" 39 | } 40 | } 41 | }, 42 | "extract-i18n": { 43 | "builder": "@angular-devkit/build-angular:extract-i18n", 44 | "options": { 45 | "browserTarget": "lazy-loading-app:build" 46 | } 47 | }, 48 | "lint": { 49 | "builder": "@angular-devkit/build-angular:tslint", 50 | "options": { 51 | "tsConfig": [ 52 | "apps/lazy-loading-app/tsconfig.app.json", 53 | "apps/lazy-loading-app/tsconfig.spec.json" 54 | ], 55 | "exclude": ["**/node_modules/**"] 56 | } 57 | }, 58 | "test": { 59 | "builder": "@angular/bazel:build", 60 | "options": { 61 | "bazelCommand": "test", 62 | "targetLabel": "//apps/lazy-loading-app/src:test" 63 | } 64 | } 65 | } 66 | }, 67 | "api": { 68 | "root": "apps/api", 69 | "sourceRoot": "apps/api/src", 70 | "projectType": "application", 71 | "prefix": "api", 72 | "schematics": {}, 73 | "architect": { 74 | "build": { 75 | "builder": "@angular/bazel:build", 76 | "options": { 77 | "targetLabel": "//apps/api/src:server", 78 | "bazelCommand": "build" 79 | }, 80 | "configurations": { 81 | "production": { 82 | "targetLabel": "//apps/api/src:server" 83 | } 84 | } 85 | }, 86 | "serve": { 87 | "builder": "@angular/bazel:build", 88 | "options": { 89 | "targetLabel": "//apps/api/src:server", 90 | "bazelCommand": "run", 91 | "watch": true 92 | }, 93 | "configurations": { 94 | "production": { 95 | "targetLabel": "//apps/api/src:server" 96 | } 97 | } 98 | }, 99 | "lint": { 100 | "builder": "@angular-devkit/build-angular:tslint", 101 | "options": { 102 | "tsConfig": [ 103 | "apps/api/tsconfig.app.json", 104 | "apps/api/tsconfig.spec.json" 105 | ], 106 | "exclude": ["**/node_modules/**"] 107 | } 108 | }, 109 | "test": { 110 | "builder": "@nrwl/builders:jest", 111 | "options": { 112 | "jestConfig": "apps/api/jest.config.js", 113 | "tsConfig": "apps/api/tsconfig.spec.json" 114 | } 115 | } 116 | } 117 | }, 118 | "api-interface": { 119 | "root": "libs/api-interface", 120 | "sourceRoot": "libs/api-interface/src", 121 | "projectType": "library", 122 | "prefix": "lazy", 123 | "architect": { 124 | "lint": { 125 | "builder": "@angular-devkit/build-angular:tslint", 126 | "options": { 127 | "tsConfig": [ 128 | "libs/api-interface/tsconfig.lib.json", 129 | "libs/api-interface/tsconfig.spec.json" 130 | ], 131 | "exclude": ["**/node_modules/**"] 132 | } 133 | }, 134 | "test": { 135 | "builder": "@nrwl/builders:jest", 136 | "options": { 137 | "jestConfig": "libs/api-interface/jest.config.js", 138 | "tsConfig": "libs/api-interface/tsconfig.spec.json" 139 | } 140 | } 141 | }, 142 | "schematics": {} 143 | } 144 | }, 145 | "cli": { 146 | "warnings": { 147 | "typescriptMismatch": false, 148 | "versionMismatch": false 149 | }, 150 | "defaultCollection": "@nrwl/schematics" 151 | }, 152 | "schematics": { 153 | "@nrwl/schematics:application": { 154 | "style": "scss", 155 | "framework": "angular" 156 | }, 157 | "@nrwl/schematics:library": { 158 | "style": "scss" 159 | } 160 | }, 161 | "defaultProject": "lazy-loading-app" 162 | } 163 | -------------------------------------------------------------------------------- /apps/api/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "api", 3 | preset: "../../jest.config.js", 4 | coverageDirectory: "../../coverage/apps/api" 5 | }; 6 | -------------------------------------------------------------------------------- /apps/api/src/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | load("@build_bazel_rules_nodejs//:defs.bzl", "nodejs_binary") 3 | load("@io_bazel_rules_docker//nodejs:image.bzl", "nodejs_image") 4 | load("@io_bazel_rules_docker//container:container.bzl", "container_image", "container_push") 5 | 6 | package(default_visibility = ["//visibility:public"]) 7 | 8 | ts_library( 9 | name = "src", 10 | srcs = glob( 11 | include = ["*.ts"], 12 | exclude = ["*.spec.ts"], 13 | ), 14 | module_name = "@api", 15 | deps = [ 16 | "//apps/api/src/app", 17 | "//apps/api/src/auth", 18 | "//apps/api/src/cats", 19 | "//apps/api/src/user", 20 | "@npm//@nestjs/common", 21 | "@npm//@nestjs/core", 22 | "@npm//@nestjs/platform-express", 23 | "@npm//@nestjs/swagger", 24 | "@npm//cors", 25 | "@npm//helmet", 26 | ], 27 | ) 28 | 29 | RUNTIME_DEPS = [ 30 | ":src", 31 | "@npm//passport-jwt", 32 | "@npm//pg", 33 | "@npm//swagger-ui-express", 34 | ] 35 | 36 | nodejs_binary( 37 | name = "server", 38 | data = RUNTIME_DEPS + ["//:env_file"], 39 | entry_point = ":main.ts", 40 | ) 41 | 42 | nodejs_image( 43 | name = "nodejs_image", 44 | data = RUNTIME_DEPS, 45 | entry_point = ":main.ts", 46 | ) 47 | 48 | # Note it will not cross-compile on Mac OS to linux: 49 | # https://github.com/bazelbuild/rules_nodejs/issues/396 50 | # https://github.com/bazelbuild/rules_nodejs/pull/827 51 | # 52 | # bazel run //apps/api/src:latest 53 | container_image( 54 | name = "latest", 55 | base = ":nodejs_image", 56 | repository = "nx_bazel_example", 57 | stamp = True, 58 | tags = ["local"], 59 | ) 60 | 61 | # IMAGE_TAG=${CIRCLE_TAG:-${CIRCLE_BRANCH}-$(echo ${CIRCLE_SHA1} | cut -c -7)} 62 | # bazel run --define push_tag=${IMAGE_TAG} --define push_repository=${REPOSITORY} //apps/api/src:push_container 63 | container_push( 64 | name = "push_container", 65 | format = "Docker", 66 | image = ":nodejs_image", 67 | registry = "gcr.io", 68 | repository = "$(push_repository)", 69 | tag = "$(push_tag)", 70 | ) 71 | -------------------------------------------------------------------------------- /apps/api/src/app/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | ts_library( 6 | name = "app", 7 | srcs = glob( 8 | include = ["**/*.ts"], 9 | exclude = ["**/*.spec.ts"], 10 | ), 11 | module_name = "@api/app", 12 | deps = [ 13 | "//apps/api/src/auth", 14 | "//apps/api/src/cats", 15 | "//apps/api/src/user", 16 | "//libs/api-interface/src", 17 | "@npm//@nestjs/common", 18 | "@npm//@nestjs/passport", 19 | "@npm//@nestjs/swagger", 20 | "@npm//@nestjs/typeorm", 21 | "@npm//@types/node", 22 | "@npm//dotenv", 23 | "@npm//tslib", 24 | "@npm//typeorm", 25 | ], 26 | ) 27 | -------------------------------------------------------------------------------- /apps/api/src/app/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from "@nestjs/testing"; 2 | 3 | import { AppController } from "./app.controller"; 4 | import { AppService } from "./app.service"; 5 | 6 | describe("AppController", () => { 7 | let app: TestingModule; 8 | 9 | beforeAll(async () => { 10 | app = await Test.createTestingModule({ 11 | controllers: [AppController], 12 | providers: [AppService] 13 | }).compile(); 14 | }); 15 | 16 | describe("getData", () => { 17 | it('should return "Welcome to api!"', () => { 18 | const appController = app.get(AppController); 19 | expect(appController.getData()).toEqual({ message: "Welcome to api!" }); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /apps/api/src/app/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, UseGuards } from '@nestjs/common'; 2 | import { AuthGuard } from '@nestjs/passport'; 3 | import { ApiBearerAuth } from '@nestjs/swagger'; 4 | 5 | import { Message } from '@api-interface'; 6 | 7 | import { AppService } from './app.service'; 8 | 9 | @ApiBearerAuth() 10 | @Controller() 11 | export class AppController { 12 | constructor(private readonly appService: AppService) {} 13 | 14 | @Get('hello') 15 | @UseGuards(AuthGuard('jwt')) 16 | getData(): Message { 17 | return this.appService.getData(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /apps/api/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Logger, Module } from '@nestjs/common'; 2 | import { PassportModule } from '@nestjs/passport'; 3 | import { TypeOrmModule } from '@nestjs/typeorm'; 4 | 5 | import { AuthModule } from '../auth/auth.module'; 6 | import { CatsModule } from '../cats/cats.module'; 7 | import { databaseConfig } from './db.config'; 8 | import { UserModule } from '../user/user.module'; 9 | import { AppController } from './app.controller'; 10 | import { AppService } from './app.service'; 11 | 12 | @Module({ 13 | imports: [ 14 | AuthModule, 15 | UserModule, 16 | CatsModule, 17 | TypeOrmModule.forRoot(databaseConfig), 18 | PassportModule.register({ defaultStrategy: 'jwt' }), 19 | ], 20 | controllers: [AppController], 21 | providers: [ 22 | AppService, 23 | Logger, 24 | ], 25 | }) 26 | export class AppModule {} 27 | -------------------------------------------------------------------------------- /apps/api/src/app/app.service.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test } from "@nestjs/testing"; 2 | 3 | import { AppService } from "./app.service"; 4 | 5 | describe("AppService", () => { 6 | let service: AppService; 7 | 8 | beforeAll(async () => { 9 | const app = await Test.createTestingModule({ 10 | providers: [AppService] 11 | }).compile(); 12 | 13 | service = app.get(AppService); 14 | }); 15 | 16 | describe("getData", () => { 17 | it('should return "Welcome to api!"', () => { 18 | expect(service.getData()).toEqual({ message: "Welcome to api!" }); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /apps/api/src/app/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Message } from '@api-interface'; 2 | import { Injectable } from '@nestjs/common'; 3 | 4 | @Injectable() 5 | export class AppService { 6 | getData(): Message { 7 | return { message: 'Welcome to api!' }; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/src/app/db.config.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | import { join } from 'path'; 3 | import { ConnectionOptions } from 'typeorm'; 4 | import { Cat } from '../cats/entities/cat'; 5 | import { User } from '../user/entities/user.entity'; 6 | 7 | dotenv.config(); 8 | 9 | const isProductionEnv = process.env.NODE_ENV === 'production'; 10 | 11 | const pathMigrations = join(__dirname, 'migrations', '**{.ts,.js}'); 12 | 13 | export const databaseConfig: ConnectionOptions = { 14 | type: 'postgres', 15 | host: process.env.DB_HOST, 16 | port: +process.env.DB_PORT, 17 | username: process.env.DB_USERNAME, 18 | password: process.env.DB_PASSWORD, 19 | database: process.env.DB_NAME, 20 | entities: [User, Cat], 21 | synchronize: true, 22 | migrations: [pathMigrations], 23 | cli: { 24 | migrationsDir: 'migrations', 25 | }, 26 | logging: !isProductionEnv ? ['error', 'migration', 'warn'] : false, 27 | }; 28 | -------------------------------------------------------------------------------- /apps/api/src/auth/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | ts_library( 6 | name = "auth", 7 | srcs = glob( 8 | include = ["**/*.ts"], 9 | exclude = ["**/*.spec.ts"], 10 | ), 11 | module_name = "@api/auth", 12 | deps = [ 13 | "//apps/api/src/user", 14 | "//libs/api-interface/src", 15 | "@npm//@nestjs/common", 16 | "@npm//@nestjs/jwt", 17 | "@npm//@nestjs/passport", 18 | "@npm//@nestjs/swagger", 19 | "@npm//@nestjs/typeorm", 20 | "@npm//@types/passport-jwt", 21 | "@npm//class-validator", 22 | "@npm//tslib", 23 | "@npm//typeorm", 24 | ], 25 | ) 26 | -------------------------------------------------------------------------------- /apps/api/src/auth/auth-config.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | import { PassportModule } from '@nestjs/passport'; 3 | 4 | @Global() 5 | @Module({ 6 | imports: [ 7 | PassportModule.register({ 8 | defaultStrategy: 'jwt', 9 | }), 10 | ], 11 | exports: [PassportModule], 12 | }) 13 | export class AuthConfigModule {} 14 | -------------------------------------------------------------------------------- /apps/api/src/auth/auth.controller.ts: -------------------------------------------------------------------------------- 1 | import { Body, Controller, HttpCode, HttpStatus, Logger, Post, Response } from '@nestjs/common'; 2 | import { SignInDto } from '../user/dto/sign-in.dto'; 3 | import { SignUpDto } from '../user/dto/sign-up.dto'; 4 | import { UserService } from '../user/user.service'; 5 | import { AuthService } from './auth.service'; 6 | 7 | @Controller('/auth') 8 | export class AuthController { 9 | 10 | constructor( 11 | private readonly authService: AuthService, 12 | private readonly userService: UserService, 13 | ) {} 14 | 15 | @Post('sign-in') 16 | @HttpCode(200) 17 | async loginUser(@Response() res: any, @Body() signInDto: SignInDto) { 18 | if (!(signInDto && signInDto.username && signInDto.password)) { 19 | return res 20 | .status(HttpStatus.FORBIDDEN) 21 | .json({ message: 'Username and password are required!' }); 22 | } 23 | 24 | const user = await this.userService.getUserByUsername(signInDto.username); 25 | 26 | if (user) { 27 | if (await UserService.compareHash(signInDto.password, user.password)) { 28 | return res 29 | .status(HttpStatus.OK) 30 | .json(await this.authService.createToken(user.username)); 31 | } 32 | } 33 | 34 | return res 35 | .status(HttpStatus.FORBIDDEN) 36 | .json({ message: 'Username or password wrong!' }); 37 | } 38 | 39 | @Post('sign-up') 40 | async registerUser(@Response() res: any, @Body() signUpDto: SignUpDto) { 41 | if (!(signUpDto && signUpDto.username && signUpDto.password)) { 42 | return res 43 | .status(HttpStatus.FORBIDDEN) 44 | .json({ message: 'Username and password are required!' }); 45 | } 46 | 47 | let user; 48 | try { 49 | user = await this.userService.getUserByUsername(signUpDto.username); 50 | } catch (err) { 51 | Logger.log('Error in lookup user', 'AuthController'); 52 | } 53 | 54 | if (user) { 55 | return res 56 | .status(HttpStatus.FORBIDDEN) 57 | .json({ message: 'Username already exists!'}); 58 | } else { 59 | user = await this.userService.createUser(signUpDto); 60 | } 61 | 62 | return res 63 | .status(HttpStatus.OK) 64 | .json(await this.authService.createToken(user.username)); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /apps/api/src/auth/auth.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { JwtModule } from '@nestjs/jwt'; 3 | 4 | import { UserModule } from '../user/user.module'; 5 | import { AuthConfigModule } from './auth-config.module'; 6 | import { AuthController } from './auth.controller'; 7 | import { AuthService } from './auth.service'; 8 | import { JwtStrategy } from './jwt.strategy'; 9 | 10 | @Module({ 11 | imports : [ 12 | AuthConfigModule, 13 | UserModule, 14 | JwtModule.register({ 15 | secretOrPrivateKey: 'secretKey', 16 | signOptions: { 17 | expiresIn: 3600, 18 | }, 19 | }), 20 | ], 21 | controllers: [ 22 | AuthController, 23 | ], 24 | providers: [ 25 | AuthService, 26 | JwtStrategy, 27 | ], 28 | exports: [ 29 | AuthService, 30 | ], 31 | }) 32 | export class AuthModule {} 33 | -------------------------------------------------------------------------------- /apps/api/src/auth/auth.service.ts: -------------------------------------------------------------------------------- 1 | import { SignedUser } from '@api-interface'; 2 | import { Injectable } from '@nestjs/common'; 3 | import { JwtService } from '@nestjs/jwt'; 4 | import { UserService } from '../user/user.service'; 5 | import { JwtPayload } from './interfaces/jwt-payload.interface'; 6 | 7 | @Injectable() 8 | export class AuthService { 9 | expiresIn = 3600; 10 | 11 | constructor( 12 | private userService: UserService, 13 | private readonly jwtService: JwtService, 14 | ) {} 15 | 16 | async createToken(username: string): Promise { 17 | const { firstName, lastName } = await this.userService.getUserByUsername(username); 18 | const payload: JwtPayload = { username, firstName, lastName }; 19 | 20 | const token = this.jwtService.sign(payload); 21 | return { 22 | expiresIn: this.expiresIn, 23 | token, 24 | username, 25 | firstName, 26 | lastName, 27 | }; 28 | } 29 | 30 | async validateUser(signedUser): Promise { 31 | if (signedUser && signedUser.username) { 32 | return Boolean(this.userService.getUserByUsername(signedUser.username)); 33 | } 34 | return false; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /apps/api/src/auth/interfaces/jwt-payload.interface.ts: -------------------------------------------------------------------------------- 1 | export interface JwtPayload { 2 | username: string; 3 | firstName: string; 4 | lastName: string; 5 | } 6 | -------------------------------------------------------------------------------- /apps/api/src/auth/jwt.strategy.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { PassportStrategy } from '@nestjs/passport'; 3 | import { ExtractJwt, Strategy } from 'passport-jwt'; 4 | import { AuthService } from './auth.service'; 5 | import { JwtPayload } from './interfaces/jwt-payload.interface'; 6 | 7 | @Injectable() 8 | export class JwtStrategy extends PassportStrategy(Strategy) { 9 | constructor(private readonly authService: AuthService) { 10 | super({ 11 | jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 12 | secretOrKey: 'secretKey', 13 | }); 14 | } 15 | 16 | async validate(payload: JwtPayload) { 17 | const user = await this.authService.validateUser(payload); 18 | if (!user) { 19 | throw new UnauthorizedException(); 20 | } 21 | return user; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /apps/api/src/cats/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | ts_library( 6 | name = "cats", 7 | srcs = glob( 8 | include = ["**/*.ts"], 9 | exclude = ["**/*.spec.ts"], 10 | ), 11 | module_name = "@api/cats", 12 | deps = [ 13 | "//libs/api-interface/src", 14 | "@npm//@nestjs/common", 15 | "@npm//@nestjs/passport", 16 | "@npm//@nestjs/swagger", 17 | "@npm//@nestjs/typeorm", 18 | "@npm//class-validator", 19 | "@npm//tslib", 20 | "@npm//typeorm", 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /apps/api/src/cats/cats.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Body, 3 | Controller, 4 | Delete, 5 | Get, 6 | HttpCode, 7 | HttpStatus, 8 | Param, 9 | ParseIntPipe, 10 | Post, 11 | UseGuards, 12 | } from '@nestjs/common'; 13 | import { AuthGuard } from '@nestjs/passport'; 14 | import { ApiBearerAuth, ApiOperation, ApiUseTags } from '@nestjs/swagger'; 15 | 16 | import { CatsService } from './cats.service'; 17 | import { CreateCatDto } from './dto/create-cat.dto'; 18 | import { Cat } from './entities/cat'; 19 | 20 | @Controller('cats') 21 | @ApiBearerAuth() 22 | @ApiUseTags('cats') 23 | @UseGuards(AuthGuard()) 24 | export class CatsController { 25 | constructor(private readonly catsService: CatsService) {} 26 | 27 | @Post() 28 | @ApiOperation({ title: 'Add cat' }) 29 | async create(@Body() createCatDto: CreateCatDto) { 30 | return this.catsService.create(createCatDto); 31 | } 32 | 33 | @Get() 34 | @ApiOperation({ title: 'Get all cats' }) 35 | async findAll(): Promise { 36 | return this.catsService.findAll(); 37 | } 38 | 39 | @Get(':id') 40 | @ApiOperation({ title: 'Get cat by id' }) 41 | async findOneById(@Param('id', ParseIntPipe) id: number) { 42 | return this.catsService.findOneById(id); 43 | } 44 | 45 | @ApiOperation({ title: 'Delete item' }) 46 | @Delete(':id') 47 | @HttpCode(HttpStatus.NO_CONTENT) 48 | async delete(@Param('id', ParseIntPipe) id: number): Promise { 49 | await this.catsService.remove(id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /apps/api/src/cats/cats.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { CatsController } from './cats.controller'; 4 | 5 | import { CatsService } from './cats.service'; 6 | import { Cat } from './entities/cat'; 7 | 8 | @Module({ 9 | imports: [ 10 | TypeOrmModule.forFeature([Cat]), 11 | ], 12 | controllers: [CatsController], 13 | providers: [CatsService], 14 | }) 15 | export class CatsModule {} 16 | -------------------------------------------------------------------------------- /apps/api/src/cats/cats.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { CreateCatDto } from './dto/create-cat.dto'; 6 | import { Cat } from './entities/cat'; 7 | 8 | @Injectable() 9 | export class CatsService { 10 | constructor( 11 | @InjectRepository(Cat) 12 | private readonly repository: Repository) { 13 | } 14 | 15 | async create(cat: CreateCatDto): Promise { 16 | return this.repository.save(cat); 17 | } 18 | 19 | async findAll(): Promise { 20 | return this.repository.find(); 21 | } 22 | 23 | async findOneById(id: number): Promise { 24 | return this.repository.findOne({ id }); 25 | } 26 | 27 | async remove(id: number): Promise { 28 | const entity = await this.findOneById(id); 29 | return this.repository.remove(entity); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /apps/api/src/cats/dto/create-cat.dto.ts: -------------------------------------------------------------------------------- 1 | import { ICreateCatDto } from '@api-interface'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | import { IsInt, IsNotEmpty, Max, Min } from 'class-validator'; 4 | 5 | export class CreateCatDto implements ICreateCatDto { 6 | @ApiModelProperty() 7 | @IsNotEmpty() 8 | name: string; 9 | 10 | @ApiModelProperty() 11 | @IsInt() 12 | @Min(1) 13 | @Max(200) 14 | age: number; 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/src/cats/entities/cat.ts: -------------------------------------------------------------------------------- 1 | import { CatModel } from '@api-interface'; 2 | import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; 3 | 4 | @Entity() 5 | export class Cat implements CatModel { 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column({ length: 500 }) 10 | name: string; 11 | 12 | @Column() 13 | age: number; 14 | } 15 | -------------------------------------------------------------------------------- /apps/api/src/main.ts: -------------------------------------------------------------------------------- 1 | import { Logger } from '@nestjs/common'; 2 | import { NestFactory } from '@nestjs/core'; 3 | import { NestExpressApplication } from '@nestjs/platform-express'; 4 | import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; 5 | import * as cors from 'cors'; 6 | import * as helmet from 'helmet'; 7 | 8 | import { AppModule } from './app/app.module'; 9 | 10 | async function bootstrap() { 11 | const app = await NestFactory.create(AppModule); 12 | app.use(cors()); 13 | app.use(helmet()); 14 | 15 | const port = process.env.port || 3000; 16 | 17 | const options = new DocumentBuilder() 18 | .setTitle('nx-bazel-starter') 19 | .setDescription('The Nx Full-Stack app API description') 20 | .addBearerAuth() 21 | .setSchemes('http', 'https') 22 | .build(); 23 | const document = SwaggerModule.createDocument(app, options); 24 | SwaggerModule.setup('api', app, document); 25 | 26 | await app.listen(port, () => { 27 | Logger.log(`App listening at http://localhost:${port}`, 'HTTP'); 28 | Logger.log('Press Ctrl+C to quit.', 'HTTP'); 29 | }); 30 | } 31 | 32 | bootstrap().catch((err) => { 33 | process.exitCode = 1; 34 | Logger.error(err); 35 | }); 36 | -------------------------------------------------------------------------------- /apps/api/src/user/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | 3 | package(default_visibility = ["//visibility:public"]) 4 | 5 | ts_library( 6 | name = "user", 7 | srcs = glob( 8 | include = ["**/*.ts"], 9 | exclude = ["**/*.spec.ts"], 10 | ), 11 | module_name = "@api/user", 12 | deps = [ 13 | "//libs/api-interface/src", 14 | "@npm//@nestjs/common", 15 | "@npm//@nestjs/passport", 16 | "@npm//@nestjs/swagger", 17 | "@npm//@nestjs/typeorm", 18 | "@npm//argon2", 19 | "@npm//class-validator", 20 | "@npm//node-gyp", 21 | "@npm//typeorm", 22 | ], 23 | ) 24 | -------------------------------------------------------------------------------- /apps/api/src/user/dto/sign-in.dto.ts: -------------------------------------------------------------------------------- 1 | import { ISignInDto } from '@api-interface'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | import { IsNotEmpty, IsString } from 'class-validator'; 4 | 5 | export class SignInDto implements ISignInDto { 6 | @ApiModelProperty() 7 | @IsString() 8 | @IsNotEmpty() 9 | username: string; 10 | 11 | @ApiModelProperty() 12 | @IsString() 13 | @IsNotEmpty() 14 | password: string; 15 | } 16 | -------------------------------------------------------------------------------- /apps/api/src/user/dto/sign-up.dto.ts: -------------------------------------------------------------------------------- 1 | import { ISignUpDto } from '@api-interface'; 2 | import { ApiModelProperty } from '@nestjs/swagger'; 3 | import { IsNotEmpty, IsString } from 'class-validator'; 4 | 5 | export class SignUpDto implements ISignUpDto { 6 | @ApiModelProperty() 7 | @IsString() 8 | @IsNotEmpty() 9 | username: string; 10 | 11 | @ApiModelProperty() 12 | @IsString() 13 | @IsNotEmpty() 14 | password: string; 15 | 16 | @ApiModelProperty() 17 | @IsString() 18 | @IsNotEmpty() 19 | firstName: string; 20 | 21 | @ApiModelProperty() 22 | @IsString() 23 | @IsNotEmpty() 24 | lastName: string; 25 | } 26 | -------------------------------------------------------------------------------- /apps/api/src/user/entities/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { UserModel } from '@api-interface'; 2 | import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm'; 3 | 4 | @Entity() 5 | export class User implements UserModel { 6 | @PrimaryGeneratedColumn() 7 | id: number; 8 | 9 | @Column({ length: 500 }) 10 | username: string; 11 | 12 | @Column({ length: 500, nullable: true }) 13 | firstName: string; 14 | 15 | @Column({ length: 500, nullable: true }) 16 | lastName: string; 17 | 18 | @Column({ length: 500 }) 19 | password: string; 20 | 21 | constructor(partialUser: Partial) { 22 | Object.assign(this, partialUser); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /apps/api/src/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | import { User } from './entities/user.entity'; 4 | import { UserService } from './user.service'; 5 | 6 | @Module({ 7 | imports: [ 8 | TypeOrmModule.forFeature([User]), 9 | ], 10 | providers: [ 11 | UserService, 12 | ], 13 | exports: [ 14 | UserService, 15 | ], 16 | }) 17 | export class UserModule {} 18 | -------------------------------------------------------------------------------- /apps/api/src/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import * as argon2 from 'argon2'; 4 | import { Repository } from 'typeorm'; 5 | import { SignUpDto } from './dto/sign-up.dto'; 6 | import { User } from './entities/user.entity'; 7 | 8 | @Injectable() 9 | export class UserService { 10 | 11 | constructor( 12 | @InjectRepository(User) 13 | private readonly userRepository: Repository) { 14 | } 15 | 16 | private static async getHash(password: string): Promise { 17 | return argon2.hash(password); 18 | } 19 | 20 | static async compareHash(password: string, hash: string): Promise { 21 | try { 22 | return await argon2.verify(hash, password); 23 | } catch (err) { 24 | return false; 25 | } 26 | } 27 | 28 | async createUser(signUpDto: SignUpDto): Promise { 29 | const password = await UserService.getHash(signUpDto.password); 30 | const user = new User({ 31 | ...signUpDto, 32 | password, 33 | }); 34 | return this.userRepository.save(user); 35 | } 36 | 37 | async getUserByUsername(username: string): Promise { 38 | return (await this.userRepository.find({ username }))[0]; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apps/api/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/apps/api", 5 | "types": ["node"] 6 | }, 7 | "exclude": ["**/*.spec.ts"], 8 | "include": ["**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["node", "jest"] 5 | }, 6 | "include": ["**/*.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /apps/api/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/apps/api", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/api/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": {} 4 | } 5 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/BUILD.bazel: -------------------------------------------------------------------------------- 1 | package(default_visibility = ["//:__subpackages__"]) 2 | 3 | # Allow any ts_library rules in this workspace to reference the config 4 | exports_files(["tsconfig.json"]) 5 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /apps/lazy-loading-app/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "lazy-loading-app", 3 | preset: "../../jest.config.js", 4 | coverageDirectory: "../../coverage/apps/lazy-loading-app/", 5 | snapshotSerializers: [ 6 | "jest-preset-angular/AngularSnapshotSerializer.js", 7 | "jest-preset-angular/HTMLCommentSerializer.js" 8 | ] 9 | }; 10 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_angular_bazel//:index.bzl", "ng_module") 2 | load("@build_bazel_rules_nodejs//:defs.bzl", "history_server", "rollup_bundle") 3 | load("@build_bazel_rules_nodejs//internal/web_package:web_package.bzl", "web_package") 4 | load("@npm_bazel_typescript//:index.bzl", "ts_devserver", "ts_library") 5 | load("@io_bazel_rules_sass//:defs.bzl", "multi_sass_binary", "sass_binary") 6 | 7 | package(default_visibility = ["//:__subpackages__"]) 8 | 9 | sass_binary( 10 | name = "styles", 11 | src = glob([ 12 | "styles.css", 13 | "styles.scss", 14 | ])[0], 15 | ) 16 | 17 | ts_library( 18 | name = "initialize_testbed", 19 | testonly = 1, 20 | srcs = [ 21 | "initialize_testbed.ts", 22 | ], 23 | deps = [ 24 | "@npm//@angular/core", 25 | "@npm//@angular/platform-browser-dynamic", 26 | "@npm//@types", 27 | ], 28 | ) 29 | 30 | ng_module( 31 | name = "src", 32 | srcs = [ 33 | "main.dev.ts", 34 | "main.prod.ts", 35 | ], 36 | tsconfig = "//:tsconfig.json", 37 | deps = [ 38 | "//apps/lazy-loading-app/src/app", 39 | "@npm//@angular/core", 40 | "@npm//@angular/forms", 41 | "@npm//@angular/platform-browser", 42 | "@npm//@angular/router", 43 | "@npm//@types", 44 | "@npm//rxjs", 45 | ], 46 | ) 47 | 48 | filegroup( 49 | name = "rxjs_umd_modules", 50 | srcs = [ 51 | ":rxjs_shims.js", 52 | "@npm//:node_modules/rxjs/bundles/rxjs.umd.js", 53 | ], 54 | ) 55 | 56 | # We always strip these paths off the front of any assets we serve 57 | _ROOT_DIRS = [ 58 | "npm/node_modules/zone.js/dist", 59 | ] 60 | 61 | # Files that we serve in both development and production 62 | _ASSETS = [ 63 | # This label references an output of the "styles" sass_binary above. 64 | ":styles.css", 65 | 66 | # We load zone.js outside the bundle. That's because it's a "pollyfill" 67 | # which speculates that such features might be available in a browser. 68 | # Also it's tricky to configure dead code elimination to understand that 69 | # zone.js is used, given that we don't have any import statement that 70 | # imports from it. 71 | "@npm//:node_modules/zone.js/dist/zone.min.js", 72 | ] 73 | 74 | # This devserver is written in Go and is super-fast. 75 | # It doesn't run any bundler or code splitter. Instead, it concatenates 76 | # named UMD and named AMD JavaScript code on-the-fly in-memory. 77 | # This scales really well for massive codebases. 78 | ts_devserver( 79 | name = "devserver", 80 | # serve these files rooted at / 81 | additional_root_paths = _ROOT_DIRS, 82 | # Serve these files but don't inject tags for them into the index file 83 | # This might be because we only want to lazy-load these scripts on-demand, 84 | # or because they aren't compatible with Require.js so we must use a runtime 85 | # loader to load them. 86 | data = [ 87 | "favicon.ico", 88 | ], 89 | entry_module = "nx_bazel_example/apps/lazy-loading-app/src/main.dev", 90 | # 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/initialize_testbed.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Provides a script to initialize TestBed before tests are run. 3 | * This file should be included in the "runtime_deps" of a "ts_web_test_suite" 4 | * rule. 5 | */ 6 | import {TestBed} from '@angular/core/testing'; 7 | import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; 8 | 9 | TestBed.initTestEnvironment(BrowserDynamicTestingModule, platformBrowserDynamicTesting()); 10 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/main.dev.ts: -------------------------------------------------------------------------------- 1 | import {platformBrowser} from '@angular/platform-browser'; 2 | // @ts-ignore 3 | import {AppModuleNgFactory} from './app/app.module.ngfactory'; 4 | 5 | platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); 6 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/main.prod.ts: -------------------------------------------------------------------------------- 1 | import {enableProdMode} from '@angular/core'; 2 | import {platformBrowser} from '@angular/platform-browser'; 3 | // @ts-ignore 4 | import {AppModuleNgFactory} from './app/app.module.ngfactory'; 5 | 6 | enableProdMode(); 7 | platformBrowser().bootstrapModuleFactory(AppModuleNgFactory); 8 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic() 12 | .bootstrapModule(AppModule) 13 | .catch((err) => console.error(err)); 14 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags.ts'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import "zone.js/dist/zone"; // Included with Angular CLI. 59 | 60 | /*************************************************************************************************** 61 | * APPLICATION IMPORTS 62 | */ 63 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/rxjs_shims.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fileoverview Provides named UMD shims for `rxjs/operators` and `rxjs/testing`. 3 | * This file should be included in the "scripts" of a "ts_devserver" 4 | * rule and the "deps" of a "ts_web_test_suite" rule. 5 | */ 6 | // rxjs/operators 7 | (function(factory) { 8 | if (typeof module === 'object' && typeof module.exports === 'object') { 9 | var v = factory(require, exports); 10 | if (v !== undefined) module.exports = v; 11 | } else if (typeof define === 'function' && define.amd) { 12 | define('rxjs/operators', ['exports', 'rxjs'], factory); 13 | } 14 | })(function(exports, rxjs) { 15 | 'use strict'; 16 | Object.keys(rxjs.operators).forEach(function(key) { 17 | exports[key] = rxjs.operators[key]; 18 | }); 19 | Object.defineProperty(exports, '__esModule', {value: true}); 20 | }); 21 | 22 | // rxjs/testing 23 | (function(factory) { 24 | if (typeof module === 'object' && typeof module.exports === 'object') { 25 | var v = factory(require, exports); 26 | if (v !== undefined) module.exports = v; 27 | } else if (typeof define === 'function' && define.amd) { 28 | define('rxjs/testing', ['exports', 'rxjs'], factory); 29 | } 30 | })(function(exports, rxjs) { 31 | 'use strict'; 32 | Object.keys(rxjs.testing).forEach(function(key) { 33 | exports[key] = rxjs.testing[key]; 34 | }); 35 | Object.defineProperty(exports, '__esModule', {value: true}); 36 | }); 37 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/styles.scss: -------------------------------------------------------------------------------- 1 | a { cursor: pointer } 2 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/src/test-setup.ts: -------------------------------------------------------------------------------- 1 | import "jest-preset-angular"; 2 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/apps/lazy-loading-app", 5 | "types": [] 6 | }, 7 | "exclude": ["src/test-setup.ts", "**/*.spec.ts"], 8 | "include": ["**/*.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["node", "jest"] 5 | }, 6 | "include": ["**/*.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/apps/lazy-loading-app/", 5 | "types": ["jest", "node"] 6 | }, 7 | "files": ["src/test-setup.ts"], 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /apps/lazy-loading-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": { 4 | "directive-selector": [true, "attribute", "lazy", "camelCase"], 5 | "component-selector": [true, "element", "lazy", "kebab-case"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /client.dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.2 2 | ENV NODE_ENV development 3 | 4 | # Set app directory 5 | WORKDIR /app 6 | 7 | # Install app dependencies 8 | COPY package.json yarn.lock angular-metadata.tsconfig.json ./ 9 | RUN yarn install --frozen-lockfile --dev 10 | 11 | COPY tsconfig.json angular.json WORKSPACE BUILD.bazel .bazelrc .bazelignore ./ 12 | 13 | # No src files are added to container here. 14 | # Dockerfile.dev is to be used with volume mounting from host via docker-compose or: 15 | # docker run -v ./apps:/app/apps:ro -v ./libs:/app/libs:ro 16 | 17 | EXPOSE 4200 18 | CMD yarn start 19 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.1' 2 | 3 | volumes: 4 | pg_data: 5 | 6 | networks: 7 | corp: 8 | 9 | services: 10 | api: 11 | build: 12 | context: . 13 | dockerfile: server.dockerfile 14 | ports: 15 | - 3000:3000 16 | networks: 17 | - corp 18 | depends_on: 19 | - postgres 20 | env_file: 21 | - .env 22 | environment: 23 | - DB_HOST=postgres 24 | volumes: 25 | - "./apps:/app/apps:ro" 26 | - "./libs:/app/libs:ro" 27 | app: 28 | build: 29 | context: . 30 | dockerfile: client.dockerfile 31 | ports: 32 | - 4200:4200 33 | networks: 34 | - corp 35 | volumes: 36 | - "./apps:/app/apps:ro" 37 | - "./libs:/app/libs:ro" 38 | postgres: 39 | image: postgres 40 | restart: always 41 | networks: 42 | - corp 43 | environment: 44 | POSTGRES_DB: bazel-nx-example 45 | POSTGRES_USER: postgres 46 | POSTGRES_PASSWORD: postgres 47 | -------------------------------------------------------------------------------- /graph.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scalio/bazel-nx-example/4067a37c045e1558316c837b39a826cc3699d698/graph.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testMatch: ["**/+(*.)+(spec|test).+(ts|js)?(x)"], 3 | transform: { 4 | "^.+\\.(ts|js|html)$": "ts-jest" 5 | }, 6 | resolver: "@nrwl/builders/plugins/jest/resolver", 7 | moduleFileExtensions: ["ts", "js", "html"], 8 | collectCoverage: true, 9 | coverageReporters: ["html"] 10 | }; 11 | -------------------------------------------------------------------------------- /libs/api-interface/README.md: -------------------------------------------------------------------------------- 1 | # ApiInterface 2 | 3 | This library was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.2.0. 4 | 5 | ## Code scaffolding 6 | 7 | Run `ng generate component component-name --project api-interface` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module --project api-interface`. 8 | 9 | > Note: Don't forget to add `--project api-interface` or else it will be added to the default project in your `angular.json` file. 10 | 11 | ## Build 12 | 13 | Run `ng build api-interface` to build the project. The build artifacts will be stored in the `dist/` directory. 14 | 15 | ## Publishing 16 | 17 | After building your library with `ng build api-interface`, go to the dist folder `cd dist/api-interface` and run `npm publish`. 18 | 19 | ## Running unit tests 20 | 21 | Run `ng test api-interface` to execute the unit tests via [Karma](https://karma-runner.github.io). 22 | 23 | ## Further help 24 | 25 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 26 | -------------------------------------------------------------------------------- /libs/api-interface/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | name: "api-interface", 3 | preset: "../../jest.config.js", 4 | coverageDirectory: "../../coverage/libs/api-interface" 5 | }; 6 | -------------------------------------------------------------------------------- /libs/api-interface/src/BUILD.bazel: -------------------------------------------------------------------------------- 1 | load("@npm_bazel_typescript//:index.bzl", "ts_library") 2 | 3 | package(default_visibility = ["//:__subpackages__"]) 4 | 5 | ts_library( 6 | name = "src", 7 | srcs = glob( 8 | include = ["**/*.ts"], 9 | exclude = ["**/*spec.ts"], 10 | ), 11 | module_name = "@api-interface", 12 | deps = ["@npm//tslib"], 13 | ) 14 | -------------------------------------------------------------------------------- /libs/api-interface/src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './interfaces/cat'; 2 | export * from './interfaces/message'; 3 | export * from './interfaces/user'; 4 | -------------------------------------------------------------------------------- /libs/api-interface/src/interfaces/cat.ts: -------------------------------------------------------------------------------- 1 | export interface CatModel { 2 | id: number; 3 | name: string; 4 | age: number; 5 | } 6 | 7 | export interface ICreateCatDto { 8 | name: string; 9 | age: number; 10 | } 11 | -------------------------------------------------------------------------------- /libs/api-interface/src/interfaces/message.ts: -------------------------------------------------------------------------------- 1 | export interface Message { 2 | message: string; 3 | } 4 | -------------------------------------------------------------------------------- /libs/api-interface/src/interfaces/user.ts: -------------------------------------------------------------------------------- 1 | export interface UserModel { 2 | id: number; 3 | username: string; 4 | firstName: string; 5 | lastName: string; 6 | password: string; 7 | } 8 | 9 | export interface ISignUpDto { 10 | username: string; 11 | password: string; 12 | firstName: string; 13 | lastName: string; 14 | } 15 | 16 | export interface ISignInDto { 17 | username: string; 18 | password: string; 19 | } 20 | 21 | export interface SignedUser { 22 | username: string; 23 | firstName: string; 24 | lastName: string; 25 | expiresIn: number; 26 | token: string; 27 | } 28 | -------------------------------------------------------------------------------- /libs/api-interface/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "compilerOptions": { 4 | "types": ["node", "jest"] 5 | }, 6 | "include": ["**/*.ts"] 7 | } 8 | -------------------------------------------------------------------------------- /libs/api-interface/tsconfig.lib.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/libs/api-interface", 5 | "target": "es2015", 6 | "module": "es2015", 7 | "moduleResolution": "node", 8 | "declaration": true, 9 | "sourceMap": true, 10 | "inlineSources": true, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "importHelpers": true, 14 | "types": [], 15 | "lib": ["dom", "es2018"] 16 | }, 17 | "exclude": ["src/test.ts", "**/*.spec.ts"] 18 | } 19 | -------------------------------------------------------------------------------- /libs/api-interface/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../../dist/out-tsc/libs/api-interface", 5 | "module": "commonjs", 6 | "types": ["jest", "node"] 7 | }, 8 | "include": ["**/*.spec.ts", "**/*.d.ts"] 9 | } 10 | -------------------------------------------------------------------------------- /libs/api-interface/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tslint.json", 3 | "rules": [] 4 | } 5 | -------------------------------------------------------------------------------- /nx.json: -------------------------------------------------------------------------------- 1 | { 2 | "npmScope": "app", 3 | "implicitDependencies": { 4 | "angular.json": "*", 5 | "package.json": "*", 6 | "tsconfig.json": "*", 7 | "tslint.json": "*", 8 | "nx.json": "*" 9 | }, 10 | "projects": { 11 | "lazy-loading-app": { 12 | "tags": [] 13 | }, 14 | "api": { 15 | "tags": [] 16 | }, 17 | "api-interface": { 18 | "tags": [] 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nx-bazel-example", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "scripts": { 6 | "ng": "ng", 7 | "postinstall": "ngc -p ./angular-metadata.tsconfig.json", 8 | "start": "ng serve", 9 | "start:prod": "ng serve --prod", 10 | "build": "ng build", 11 | "test": "ng test", 12 | "lint": "./node_modules/.bin/nx lint && ng lint", 13 | "affected:apps": "./node_modules/.bin/nx affected:apps", 14 | "affected:libs": "./node_modules/.bin/nx affected:libs", 15 | "affected:build": "./node_modules/.bin/nx affected:build", 16 | "affected:e2e": "./node_modules/.bin/nx affected:e2e", 17 | "affected:test": "./node_modules/.bin/nx affected:test", 18 | "affected:lint": "./node_modules/.bin/nx affected:lint", 19 | "affected:dep-graph": "./node_modules/.bin/nx affected:dep-graph", 20 | "affected": "./node_modules/.bin/nx affected", 21 | "format": "./node_modules/.bin/nx format:write", 22 | "format:write": "./node_modules/.bin/nx format:write", 23 | "format:check": "./node_modules/.bin/nx format:check", 24 | "update": "ng update @nrwl/schematics", 25 | "update:check": "ng update", 26 | "workspace-schematic": "./node_modules/.bin/nx workspace-schematic", 27 | "dep-graph": "./node_modules/.bin/nx dep-graph", 28 | "help": "./node_modules/.bin/nx help" 29 | }, 30 | "private": true, 31 | "dependencies": { 32 | "@angular/animations": "^8.2.0", 33 | "@angular/common": "^8.2.0", 34 | "@angular/compiler": "^8.2.0", 35 | "@angular/core": "^8.2.0", 36 | "@angular/forms": "^8.2.0", 37 | "@angular/platform-browser": "^8.2.0", 38 | "@angular/platform-browser-dynamic": "^8.2.0", 39 | "@angular/router": "^8.2.0", 40 | "@nestjs/common": "6.5.2", 41 | "@nestjs/core": "6.5.2", 42 | "@nestjs/jwt": "^6.1.1", 43 | "@nestjs/passport": "^6.1.0", 44 | "@nestjs/platform-express": "^6.5.2", 45 | "@nestjs/swagger": "^3.1.0", 46 | "@nestjs/typeorm": "^6.1.3", 47 | "@nrwl/angular": "^8.4.4", 48 | "@nrwl/nest": "^8.4.4", 49 | "@nrwl/workspace": "^8.4.4", 50 | "argon2": "^0.24.0", 51 | "class-transformer": "^0.2.3", 52 | "class-validator": "^0.9.1", 53 | "core-js": "2.6.9", 54 | "cors": "^2.8.5", 55 | "dotenv": "^8.0.0", 56 | "helmet": "^3.19.0", 57 | "passport": "^0.4.0", 58 | "passport-jwt": "^4.0.0", 59 | "pg": "^7.11.0", 60 | "reflect-metadata": "0.1.13", 61 | "rxjs": "6.5.2", 62 | "swagger-ui-express": "^4.0.7", 63 | "systemjs": "0.21.6", 64 | "typeorm": "^0.2.18", 65 | "zone.js": "~0.10.1" 66 | }, 67 | "devDependencies": { 68 | "@angular-devkit/build-angular": "~0.802.0", 69 | "@angular/bazel": "^8.2.0", 70 | "@angular/cli": "^8.2.0", 71 | "@angular/compiler-cli": "^8.2.0", 72 | "@angular/language-service": "^8.2.0", 73 | "@bazel/bazel": "^0.28.1", 74 | "@bazel/hide-bazel-files": "0.35.0", 75 | "@bazel/ibazel": "^0.10.2", 76 | "@bazel/karma": "0.35.0", 77 | "@bazel/typescript": "0.35.0", 78 | "@nestjs/schematics": "6.3.0", 79 | "@nestjs/testing": "6.5.2", 80 | "@types/jest": "24.0.15", 81 | "@types/jquery": "3.3.30", 82 | "@types/node": "~12.6.3", 83 | "@types/passport-jwt": "^3.0.1", 84 | "codelyzer": "~5.1.0", 85 | "cypress": "3.4.0", 86 | "eslint": "^6.1.0", 87 | "jest": "24.8.0", 88 | "jest-preset-angular": "7.1.1", 89 | "node-sass": "^4.12.0", 90 | "prettier": "1.18.2", 91 | "ts-jest": "24.0.2", 92 | "ts-node": "~8.3.0", 93 | "tslint": "~5.18.0", 94 | "typescript": "3.4.5" 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /server.dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:12.2 2 | ENV NODE_ENV development 3 | 4 | # Set app directory 5 | WORKDIR /app 6 | 7 | # Install app dependencies 8 | COPY package.json yarn.lock angular-metadata.tsconfig.json ./ 9 | RUN yarn install --frozen-lockfile --dev 10 | 11 | COPY tsconfig.json angular.json WORKSPACE BUILD.bazel .bazelrc .bazelignore ./ 12 | 13 | # No src files are added to container here. 14 | # Dockerfile.dev is to be used with volume mounting from host via docker-compose or: 15 | # docker run -v ./apps:/app/apps:ro -v ./libs:/app/libs:ro 16 | 17 | EXPOSE 3000 18 | CMD yarn start api 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "downlevelIteration": true, 5 | "sourceMap": true, 6 | "declaration": false, 7 | "moduleResolution": "node", 8 | "emitDecoratorMetadata": true, 9 | "experimentalDecorators": true, 10 | "importHelpers": true, 11 | "target": "es6", 12 | "module": "esnext", 13 | "typeRoots": [ 14 | "node_modules/@types" 15 | ], 16 | // "lib": [ 17 | // "dom", 18 | // "es5", 19 | // "es2015.collection", 20 | // "es2015.iterable", 21 | // "es2015.promise" 22 | // ], 23 | "rootDirs": [ 24 | "./apps/lazy-loading-app/src", 25 | "./dist/bin/apps/lazy-loading-app/src" 26 | ], 27 | "lib": [ 28 | "es2017", 29 | "dom", 30 | "esnext.asynciterable" 31 | ], 32 | "skipLibCheck": true, 33 | "skipDefaultLibCheck": true, 34 | "baseUrl": ".", 35 | "paths": { 36 | "@api-interface": [ 37 | "libs/api-interface/src/index.ts" 38 | ] 39 | } 40 | }, 41 | "exclude": [ 42 | "node_modules", 43 | "tmp" 44 | ] 45 | } 46 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "node_modules/codelyzer" 5 | ], 6 | "rules": { 7 | "arrow-return-shorthand": true, 8 | "callable-types": true, 9 | "class-name": true, 10 | "deprecation": { 11 | "severity": "warn" 12 | }, 13 | "forin": true, 14 | "import-blacklist": [true, "rxjs/Rx"], 15 | "interface-name": false, 16 | "interface-over-type-literal": true, 17 | "max-classes-per-file": false, 18 | "member-access": false, 19 | "member-ordering": [ 20 | true, 21 | { 22 | "order": [ 23 | "static-field", 24 | "instance-field", 25 | "static-method", 26 | "instance-method" 27 | ] 28 | } 29 | ], 30 | "no-arg": true, 31 | "no-bitwise": true, 32 | "no-conflicting-lifecycle": true, 33 | "no-console": [true, "debug", "info", "time", "timeEnd", "trace"], 34 | "no-construct": true, 35 | "no-debugger": true, 36 | "no-duplicate-super": true, 37 | "no-empty": false, 38 | "no-empty-interface": true, 39 | "no-eval": true, 40 | "no-inferrable-types": [true, "ignore-params"], 41 | "no-misused-new": true, 42 | "no-non-null-assertion": true, 43 | "no-shadowed-variable": true, 44 | "no-string-literal": false, 45 | "no-string-throw": true, 46 | "no-switch-case-fall-through": true, 47 | "no-unnecessary-initializer": true, 48 | "no-unused-expression": true, 49 | "no-use-before-declare": true, 50 | "no-var-keyword": true, 51 | "object-literal-sort-keys": false, 52 | "prefer-const": true, 53 | "quotemark": [true, "single"], 54 | "radix": true, 55 | "triple-equals": [true, "allow-null-check"], 56 | "unified-signatures": true, 57 | "variable-name": false, 58 | "nx-enforce-module-boundaries": [ 59 | true, 60 | { 61 | "allow": [], 62 | "depConstraints": [ 63 | { 64 | "sourceTag": "*", 65 | "onlyDependOnLibsWithTags": ["*"] 66 | } 67 | ] 68 | } 69 | ], 70 | "directive-selector": [true, "attribute", "app", "camelCase"], 71 | "component-selector": [true, "element", "app", "kebab-case"], 72 | "no-output-on-prefix": true, 73 | "no-inputs-metadata-property": true, 74 | "no-outputs-metadata-property": true, 75 | "no-host-metadata-property": true, 76 | "no-input-rename": true, 77 | "no-output-rename": true, 78 | "use-lifecycle-interface": true, 79 | "use-pipe-transform-interface": true, 80 | "component-class-suffix": true, 81 | "directive-class-suffix": true 82 | } 83 | } 84 | --------------------------------------------------------------------------------