├── .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 | 
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 |