mirror of
https://github.com/nestjs/nest.git
synced 2026-02-27 11:11:16 +00:00
Compare commits
2 Commits
master
...
fix/fastif
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5612e0197 | ||
|
|
6c54448bf7 |
@@ -1,35 +1,13 @@
|
||||
version: 2.1
|
||||
|
||||
parameters:
|
||||
check-legacy-node-version:
|
||||
type: boolean
|
||||
default: false
|
||||
legacy-node-version:
|
||||
type: string
|
||||
default: '18.20'
|
||||
maintenance-node-version:
|
||||
type: string
|
||||
default: '20.18'
|
||||
active-node-version:
|
||||
type: string
|
||||
default: '22.11'
|
||||
current-node-version:
|
||||
type: string
|
||||
default: '23.3'
|
||||
version: 2
|
||||
|
||||
aliases:
|
||||
- &restore-cache
|
||||
restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- &save-cache
|
||||
save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- &install-deps
|
||||
run:
|
||||
name: Install dependencies
|
||||
command: npm install --legacy-peer-deps
|
||||
command: npm ci --legacy-peer-deps
|
||||
- &build-packages
|
||||
run:
|
||||
name: Build
|
||||
@@ -39,96 +17,80 @@ aliases:
|
||||
name: Test
|
||||
command: npm run test
|
||||
|
||||
unit-tests-template: &unit-tests-template
|
||||
working_directory: ~/nest
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- *run-unit-tests
|
||||
|
||||
jobs:
|
||||
build:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
name: Update NPM version
|
||||
command: 'sudo npm install -g npm@^9'
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *save-cache
|
||||
- *build-packages
|
||||
command: 'sudo npm install -g npm@^8'
|
||||
- restore_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
- run:
|
||||
name: Install dependencies
|
||||
command: npm ci --legacy-peer-deps
|
||||
- save_cache:
|
||||
key: dependency-cache-{{ checksum "package.json" }}
|
||||
paths:
|
||||
- ./node_modules
|
||||
- run:
|
||||
name: Build
|
||||
command: npm run build
|
||||
|
||||
test:
|
||||
parameters:
|
||||
node-version:
|
||||
type: string
|
||||
test_node_16:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< parameters.node-version >>
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- when:
|
||||
condition:
|
||||
and:
|
||||
- equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
]
|
||||
- not: << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- run:
|
||||
name: Skip
|
||||
command: |
|
||||
echo Skipping
|
||||
- when:
|
||||
condition:
|
||||
or:
|
||||
- not:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
]
|
||||
- << pipeline.parameters.check-legacy-node-version >>
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- when:
|
||||
condition:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- run:
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
- when:
|
||||
condition:
|
||||
not:
|
||||
equal:
|
||||
[
|
||||
'<< parameters.node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
]
|
||||
steps:
|
||||
- *run-unit-tests
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- run:
|
||||
name: Test (coverage)
|
||||
command: npm run test:cov
|
||||
- run:
|
||||
name: Collect coverage
|
||||
command: npm run coverage
|
||||
- store_artifacts:
|
||||
path: coverage
|
||||
|
||||
test_node_18:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: cimg/node:18.14
|
||||
|
||||
test_node_19:
|
||||
<<: *unit-tests-template
|
||||
docker:
|
||||
- image: cimg/node:19.8
|
||||
|
||||
lint:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.active-node-version >>
|
||||
- image: circleci/node:16
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- run:
|
||||
name: Lint
|
||||
command: npm run lint:ci
|
||||
command: npm run lint
|
||||
- run:
|
||||
name: Lint commit
|
||||
command: ./node_modules/.bin/commitlint-circle -c .commitlintrc.json
|
||||
|
||||
integration_tests:
|
||||
working_directory: ~/nest
|
||||
@@ -143,9 +105,9 @@ jobs:
|
||||
- run:
|
||||
name: Upgrade Node.js
|
||||
command: |
|
||||
nvm install << pipeline.parameters.maintenance-node-version >>
|
||||
nvm install v16
|
||||
node -v
|
||||
nvm alias default << pipeline.parameters.maintenance-node-version >>
|
||||
nvm alias default v16
|
||||
- run:
|
||||
name: Install Docker Compose
|
||||
command: |
|
||||
@@ -165,16 +127,28 @@ jobs:
|
||||
name: Integration tests
|
||||
command: npm run test:integration
|
||||
|
||||
codechecks_benchmarks:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:16.19
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
- *install-deps
|
||||
- *build-packages
|
||||
- run:
|
||||
name: Install native wrk
|
||||
command: .circleci/install-wrk.sh
|
||||
- run:
|
||||
name: Run codechecks with benchmarks
|
||||
command: yarn codechecks:benchmarks
|
||||
|
||||
samples:
|
||||
working_directory: ~/nest
|
||||
docker:
|
||||
- image: cimg/node:<< pipeline.parameters.maintenance-node-version >>
|
||||
- image: redis:8-alpine
|
||||
name: redis
|
||||
- image: cimg/node:16.19
|
||||
environment:
|
||||
DISABLE_OPENCOLLECTIVE: 'true'
|
||||
REDIS_HOST: redis
|
||||
REDIS_PORT: 6379
|
||||
- DISABLE_OPENCOLLECTIVE: true
|
||||
steps:
|
||||
- checkout
|
||||
- *restore-cache
|
||||
@@ -183,23 +157,17 @@ jobs:
|
||||
name: Build all samples
|
||||
command: npm run build:samples
|
||||
|
||||
|
||||
workflows:
|
||||
version: 2
|
||||
build-and-test:
|
||||
jobs:
|
||||
- build
|
||||
- test:
|
||||
- test_node_16:
|
||||
requires:
|
||||
- build
|
||||
- test_node_18:
|
||||
requires:
|
||||
- build
|
||||
matrix:
|
||||
parameters:
|
||||
node-version:
|
||||
[
|
||||
'<< pipeline.parameters.legacy-node-version >>',
|
||||
'<< pipeline.parameters.maintenance-node-version >>',
|
||||
'<< pipeline.parameters.active-node-version >>',
|
||||
'<< pipeline.parameters.current-node-version >>',
|
||||
]
|
||||
- lint:
|
||||
requires:
|
||||
- build
|
||||
@@ -209,4 +177,6 @@ workflows:
|
||||
- samples:
|
||||
requires:
|
||||
- build
|
||||
|
||||
- codechecks_benchmarks:
|
||||
requires:
|
||||
- build
|
||||
|
||||
13
.circleci/install-wrk.sh
Executable file
13
.circleci/install-wrk.sh
Executable file
@@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
|
||||
# based on https://medium.com/@felipedutratine/intelligent-benchmark-with-wrk-163986c1587f
|
||||
|
||||
cd /tmp/
|
||||
sudo apt-get install build-essential libssl-dev git -y
|
||||
git clone https://github.com/wg/wrk.git wrk
|
||||
cd wrk
|
||||
sudo make
|
||||
# move the executable to somewhere in your PATH, ex:
|
||||
sudo cp wrk /usr/local/bin
|
||||
@@ -37,8 +37,7 @@
|
||||
"socket.io",
|
||||
"ws",
|
||||
"testing",
|
||||
"websockets",
|
||||
"release"
|
||||
"websockets"
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
**/node_modules/**
|
||||
*.d.ts
|
||||
*.js
|
||||
46
.eslintrc.js
Normal file
46
.eslintrc.js
Normal file
@@ -0,0 +1,46 @@
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'prettier',
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
files: ['**/*.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['**/*.spec.ts', 'integration/**/*.ts'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.spec.json',
|
||||
sourceType: 'module',
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
},
|
||||
}
|
||||
]
|
||||
};
|
||||
50
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
50
.github/ISSUE_TEMPLATE/Bug_report.yml
vendored
@@ -1,13 +1,12 @@
|
||||
name: "\U0001F41B Bug Report"
|
||||
description: "If something isn't working as expected \U0001F914"
|
||||
labels: ["needs triage"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
@@ -39,19 +38,15 @@ body:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Minimum reproduction code"
|
||||
description: "An URL to some Git repository/[StackBlitz](https://stackblitz.com/fork/github/nestjs/typescript-starter)/[CodeSandbox](https://codesandbox.io/s/github/nestjs/typescript-starter/tree/master) project that reproduces your issue. [Wtf is a minimum reproduction?](https://jmcdo29.github.io/wtf-is-a-minimum-reproduction)"
|
||||
placeholder: "https://github.com/..."
|
||||
description: |
|
||||
An URL to some Git repository/[StackBlitz](https://stackblitz.com/fork/github/nestjs/typescript-starter)/[CodeSandbox](https://codesandbox.io/s/github/nestjs/typescript-starter/tree/master) project that reproduces your issue. [What is a minimum reproduction?](https://jmcdo29.github.io/wtf-is-a-minimum-reproduction)
|
||||
|
||||
> [!WARNING]
|
||||
> We may close this Issue if we don't manage to reproduce the potential bug. [Read this](https://antfu.me/posts/why-reproductions-are-required) to understand why.
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: "Steps to reproduce"
|
||||
description: |
|
||||
How the issue manifests?
|
||||
You could leave this blank if you already write this in your reproduction code
|
||||
You could leave this blank if you alread write this in your reproduction code
|
||||
placeholder: |
|
||||
1. `npm ci`
|
||||
2. `npm start:dev`
|
||||
@@ -64,13 +59,46 @@ body:
|
||||
label: "Expected behavior"
|
||||
description: "A clear and concise description of what you expected to happened (or code)"
|
||||
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
---
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Package"
|
||||
description: |
|
||||
Which package (or packages) do you think your issue is related to?
|
||||
**Tip**: The first line of the stack trace can help you to figure out this
|
||||
|
||||
The package isn't listed below? Try to find its repository [here](https://github.com/orgs/nestjs/repositories) and open the issue there instead
|
||||
options:
|
||||
- label: "I don't know. Or some 3rd-party package"
|
||||
- label: "<code>@nestjs/common</code>"
|
||||
- label: "<code>@nestjs/core</code>"
|
||||
- label: "<code>@nestjs/microservices</code>"
|
||||
- label: "<code>@nestjs/platform-express</code>"
|
||||
- label: "<code>@nestjs/platform-fastify</code>"
|
||||
- label: "<code>@nestjs/platform-socket.io</code>"
|
||||
- label: "<code>@nestjs/platform-ws</code>"
|
||||
- label: "<code>@nestjs/testing</code>"
|
||||
- label: "<code>@nestjs/websockets</code>"
|
||||
- label: "Other (see below)"
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "Other package"
|
||||
description: "If your issue is related to some package that is not listed above nor under @nestjs org, write its name here"
|
||||
|
||||
- type: input
|
||||
attributes:
|
||||
label: "NestJS version"
|
||||
description: |
|
||||
Which exact version of `@nestjs/core` package are you using?
|
||||
Which version of `@nestjs/core` are you using?
|
||||
**Tip**: Make sure that all of yours `@nestjs/*` dependencies are in sync!
|
||||
placeholder: "10.0.0"
|
||||
placeholder: "8.1.3"
|
||||
|
||||
- type: textarea
|
||||
validations:
|
||||
@@ -89,7 +117,7 @@ body:
|
||||
attributes:
|
||||
label: "Node.js version"
|
||||
description: "Which version of Node.js are you using?"
|
||||
placeholder: "24.0.0"
|
||||
placeholder: "14.17.6"
|
||||
|
||||
- type: checkboxes
|
||||
validations:
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
3
.github/ISSUE_TEMPLATE/Feature_request.yml
vendored
@@ -1,13 +1,12 @@
|
||||
name: "\U0001F680 Feature Request"
|
||||
description: "I have a suggestion \U0001F63B!"
|
||||
labels: ["type: enhancement :wolf:", "needs triage"]
|
||||
type: feature
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/Regression.yml
vendored
3
.github/ISSUE_TEMPLATE/Regression.yml
vendored
@@ -1,13 +1,12 @@
|
||||
name: "\U0001F4A5 Regression"
|
||||
description: "Report an unexpected while upgrading your Nest application!"
|
||||
labels: ["type: bug :sob:", "needs triage"]
|
||||
type: bug
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## :warning: We use GitHub Issues to track bug reports, feature requests and regressions
|
||||
|
||||
|
||||
If you are not sure that your issue is a bug, you could:
|
||||
|
||||
- read the [FAQ's common errors](https://docs.nestjs.com/faq/common-errors) page
|
||||
|
||||
@@ -2,7 +2,6 @@ title: "perf: "
|
||||
name: "\U0001F525 Suggestion for Improving Performance"
|
||||
description: "I have a suggestion that might improve the performance of Nest \U00002728"
|
||||
labels: ["type: enhancement :wolf:", "needs triage"]
|
||||
type: task
|
||||
body:
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
|
||||
8
.github/workflows/codeql-analysis.yml
vendored
8
.github/workflows/codeql-analysis.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v4
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
queries: +security-extended
|
||||
# Override language selection by uncommenting this and choosing your languages
|
||||
@@ -44,7 +44,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v4
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -58,4 +58,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v4
|
||||
uses: github/codeql-action/analyze@v2
|
||||
|
||||
10
.gitignore
vendored
10
.gitignore
vendored
@@ -1,6 +1,3 @@
|
||||
packages/*/package-lock.json
|
||||
sample/**/package-lock.json
|
||||
|
||||
# dependencies
|
||||
node_modules/
|
||||
|
||||
@@ -9,9 +6,6 @@ node_modules/
|
||||
/.awcache
|
||||
/.vscode
|
||||
/.devcontainer
|
||||
/.classpath
|
||||
/.project
|
||||
/.settings
|
||||
*.code-workspace
|
||||
|
||||
# Vim
|
||||
@@ -34,7 +28,6 @@ yarn-error.log
|
||||
/packages/**/.npmignore
|
||||
/packages/**/LICENSE
|
||||
*.tsbuildinfo
|
||||
/.nx/workspace-data
|
||||
|
||||
# example
|
||||
/quick-start
|
||||
@@ -51,5 +44,4 @@ yarn-error.log
|
||||
build/config\.gypi
|
||||
|
||||
.npmrc
|
||||
pnpm-lock.yaml
|
||||
/.history
|
||||
pnpm-lock.yaml
|
||||
@@ -1 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit $1
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx lint-staged
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
packages/**/*.d.ts
|
||||
packages/**/*.js
|
||||
.nyc_output
|
||||
|
||||
/.nx/workspace-data
|
||||
@@ -25,7 +25,7 @@ Stack Overflow is a much better place to ask questions since:
|
||||
|
||||
<!-- - there are thousands of people willing to help on Stack Overflow [maybe one day] -->
|
||||
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else.
|
||||
- questions and answers stay available for public viewing so your question / answer might help someone else
|
||||
- Stack Overflow's voting system assures that the best answers are prominently visible.
|
||||
|
||||
To save your and our time, we will systematically close all issues that are requests for general support and redirect people to Stack Overflow.
|
||||
@@ -164,7 +164,7 @@ You will need [Node.js](https://nodejs.org) version >= 10.13.0 (except for v13).
|
||||
1. After cloning the repo, run:
|
||||
|
||||
```bash
|
||||
$ npm ci --legacy-peer-deps # (or yarn install)
|
||||
$ npm ci # (or yarn install)
|
||||
```
|
||||
|
||||
2. In order to prepare your environment run `prepare.sh` shell script:
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2017-present Kamil Mysliwiec <https://kamilmysliwiec.com>
|
||||
Copyright (c) 2017-2023 Kamil Mysliwiec <https://kamilmysliwiec.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
165
Readme.md
165
Readme.md
@@ -1,5 +1,5 @@
|
||||
<p align="center">
|
||||
<a href="https://nestjs.com/" target="_blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
<a href="https://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
|
||||
</p>
|
||||
|
||||
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||
@@ -8,7 +8,7 @@
|
||||
<p align="center">A progressive <a href="https://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||
<p align="center">
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||
<a href="https://github.com/nestjs/nest/blob/master/LICENSE" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||
@@ -23,21 +23,21 @@
|
||||
|
||||
## Description
|
||||
|
||||
Nest is a framework for building efficient, scalable <a href="https://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="https://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
Nest is a framework for building efficient, scalable <a href="http://nodejs.org" target="_blank">Node.js</a> server-side applications. It uses modern JavaScript, is built with <a href="http://www.typescriptlang.org" target="_blank">TypeScript</a> (preserves compatibility with pure JavaScript) and combines elements of OOP (Object Oriented Programming), FP (Functional Programming), and FRP (Functional Reactive Programming).
|
||||
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also provides compatibility with a wide range of other libraries, like <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad of third-party plugins which are available.</p>
|
||||
<p>Under the hood, Nest makes use of <a href="https://expressjs.com/" target="_blank">Express</a>, but also, provides compatibility with a wide range of other libraries, like e.g. <a href="https://github.com/fastify/fastify" target="_blank">Fastify</a>, allowing for easy use of the myriad third-party plugins which are available.</p>
|
||||
|
||||
## Philosophy
|
||||
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a>, and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a>, which improve developer productivity and enable the construction of fast, testable, and extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers, and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, and loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
<p>In recent years, thanks to Node.js, JavaScript has become the “lingua franca” of the web for both front and backend applications, giving rise to awesome projects like <a href="https://angular.io/" target="_blank">Angular</a>, <a href="https://github.com/facebook/react" target="_blank">React</a> and <a href="https://github.com/vuejs/vue" target="_blank">Vue</a> which improve developer productivity and enable the construction of fast, testable, extensible frontend applications. However, on the server-side, while there are a lot of superb libraries, helpers and tools for Node, none of them effectively solve the main problem - the architecture.</p>
|
||||
<p>Nest aims to provide an application architecture out of the box which allows for effortless creation of highly testable, scalable, loosely coupled and easily maintainable applications. The architecture is heavily inspired by Angular.</p>
|
||||
|
||||
## Getting started
|
||||
|
||||
- To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
- 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
- [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
- [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
* To check out the [guide](https://docs.nestjs.com), visit [docs.nestjs.com](https://docs.nestjs.com). :books:
|
||||
* 要查看中文 [指南](readme_zh.md), 请访问 [docs.nestjs.cn](https://docs.nestjs.cn). :books:
|
||||
* [가이드](readme_kr.md) 문서는 [docs.nestjs.com](https://docs.nestjs.com)에서 확인하실 수 있습니다. :books:
|
||||
* [ガイド](readme_jp.md)は [docs.nestjs.com](https://docs.nestjs.com)でご確認ください。 :books:
|
||||
|
||||
## Questions
|
||||
|
||||
@@ -49,87 +49,96 @@ Please make sure to read the [Issue Reporting Checklist](https://github.com/nest
|
||||
|
||||
## Consulting
|
||||
|
||||
With official support, you can get expert help straight from the Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
With official support, you can get expert help straight from Nest core team. We provide dedicated technical support, migration strategies, advice on best practices (and design decisions), PR reviews, and team augmentation. Read more about [support here](https://enterprise.nestjs.com).
|
||||
|
||||
## Support
|
||||
|
||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support from the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||
|
||||
#### Principal Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://microsoft.com/" target="_blank"><img src="https://nestjs.com/img/logos/microsoft-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://mojam.co" target="_blank"><img src="https://nestjs.com/img/logos/mojam-logo.png" width="80" valign="middle" /></a></td>
|
||||
<td><a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td>
|
||||
<td><a href="https://serpapi.com/" target="_blank"><img src="https://nestjs.com/img/logos/serpapi-logo.png" width="150" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr>
|
||||
<td><a href="https://trilon.io" target="_blank"><img src="https://nestjs.com/img/trilon.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://valor-software.com/" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/valor-software.png" width="170" valign="middle" /></a></td><td>
|
||||
<a href="https://amplication.com/" target="_blank"><img src="https://nestjs.com/img/amplication-logo.svg" width="190" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
#### Gold Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.redhat.com" target="_blank"><img src="https://nestjs.com/img/logos/red-hat-logo.svg" width="200" valign="middle" /></a></td>
|
||||
<td><a href="https://github.com/Sanofi-IADC" target="_blank"><img src="https://docs.nestjs.com/assets/sponsors/sanofi.png" width="180" valign="middle" /></a></td>
|
||||
<td><a href="https://nx.dev" target="_blank"><img src="https://nestjs.com/img/logos/nx-logo.png" height="45" valign="middle" /></a></td>
|
||||
<td><a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/logos/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td><a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/logos/jetbrains-logo.svg" width="90" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/logos/snyk-logo-black.png" width="185" valign="middle" /></a></td>
|
||||
<td><a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/logos/movavi-logo.svg" width="105" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://weld.app/" target="_blank"><img src="https://nestjs.com/img/weld-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://intrinsic.ventures/" target="_blank"><img src="https://nestjs.com/img/intrinisic-logo.png" width="210" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://jetbrains.com/" target="_blank"><img src="https://nestjs.com/img/jetbrains-logo.svg" width="110" valign="middle" /></a></td><td>
|
||||
<a href="https://snyk.co/nestjs" target="_blank"><img src="https://nestjs.com/img/snyk-logo-black.png" width="185" valign="middle" /></a></td><td>
|
||||
<a href="https://fuseautotech.com/" target="_blank"><img src="https://nestjs.com/img/fuse-logo.svg" width="105" valign="middle" /></a></td>
|
||||
<td>
|
||||
<a href="https://ridicorp.com/career/" target="_blank"><img src="https://nestjs.com/img/ridi-logo.svg" width="105" valign="middle" /></a></td><td>
|
||||
<a href="https://www.movavi.com/imovie-for-windows.html" target="_blank"><img src="https://nestjs.com/img/movavi-logo.svg" width="105" valign="middle" /></a></td></</tr></table>
|
||||
|
||||
#### Silver Sponsors
|
||||
|
||||
<table style="text-align:center;">
|
||||
<tr>
|
||||
<td><a href="https://www.mercedes-benz.com/" target="_blank"><img src="https://nestjs.com/img/logos/mercedes-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td><a href="https://www.dinii.jp/" target="_blank"><img src="https://nestjs.com/img/logos/dinii-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td><a href="https://handsontable.com/docs/react-data-grid/?utm_source=NestJS_GH&utm_medium=sponsorship&utm_campaign=library_sponsorship_2024" target="_blank"><img src="https://nestjs.com/img/logos/handsontable-dark-logo.svg#2" width="150" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.itflashcards.com/" target="_blank"><img src="https://nestjs.com/img/logos/it_flashcards-logo.png" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://arcjet.com/?ref=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/arcjet-logo.svg" width="170" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://crawljobs.com" target="_blank"><img src="https://nestjs.com/img/logos/crawljobs-logo.svg" width="130" valign="middle" /></a></td>
|
||||
</tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://pandektes.com" target="_blank"><img src="https://nestjs.com/img/logos/pandektes-logo.png" width="65" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<table style="text-align:center;"><tr><td>
|
||||
<a href="https://neoteric.eu/" target="_blank"><img src="https://nestjs.com/img/neoteric-cut.png" width="120" valign="middle" /></a> </td><td>
|
||||
<a href="http://gojob.com" target="_blank"><img src="http://nestjs.com/img/gojob-logo.png" valign="middle" width="100" /></a> </td><td>
|
||||
<a href="https://www.myleodsc.com/" target="_blank"><img src="https://nestjs.com/img/myleo-logo.png" width="180" valign="middle" /></td><td>
|
||||
<a href="https://careers.meetdandy.com/?gh_src=063ba61e3us" target="_blank"><img src="https://nestjs.com/img/dandy-roles-logo.svg" width="150" valign="middle" /></td><td>
|
||||
<a href="https://www.castlecraft.in" target="_blank"><img src="https://nestjs.com/img/castlecraft-logo.png" width="150" valign="middle" /></td>
|
||||
<td><a href="https://www.tinystacks.com" target="_blank"><img src="https://nestjs.com/img/tinystacks-logo.png#1" width="140" valign="middle" /></td>
|
||||
<td><a href="https://n.inc" target="_blank"><img src="https://nestjs.com/img/n-inc-logo.svg" width="120" valign="middle" /></td></tr><tr>
|
||||
<td><a href="https://bilberrry.com/" target="_blank"><img src="https://nestjs.com/img/bilberrry-logo.svg" width="180" valign="middle" /></td>
|
||||
<td><a href="https://ipinfo.ai/" target="_blank"><img src="https://nestjs.com/img/ipinfo-logo.png" width="130" valign="middle" /></td>
|
||||
<td><a href="https://chax.at" target="_blank"><img src="https://nestjs.com/img/chaxat-logo.png" width="100" valign="middle" /></td></tr>
|
||||
</table>
|
||||
|
||||
#### Sponsors
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/logos/swingdev-logo.svg#1" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/novologic.png" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/logos/mantro-logo.svg" width="95" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/logos/triplebyte.png" width="107" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/logos/nearpod-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/logos/genuinebee.svg" width="97" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/logos/vpn-review-logo.png" width="85" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/logos/lambda-it-logo.svg" width="115" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/logos/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/logos/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/logos/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/logos/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/logos/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/logos/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/logos/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/logos/dopiosh-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/logos/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.tripoffice.com/" target="_blank"><img src="https://nestjs.com/img/logos/tripoffice-logo.png" width="140" valign="middle" /></a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" valign="middle"><a href="https://solcellsforetag.se/" target="_blank"><img src="https://nestjs.com/img/logos/solcellsforetag-logo.svg" width="140" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.route4me.com/" target="_blank"><img src="https://nestjs.com/img/logos/route4me-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
<table><tr><td align="center" valign="middle">
|
||||
<a href="https://www.swingdev.io" target="_blank"><img src="https://nestjs.com/img/swingdev-logo.svg#1" width="110" valign="middle" /> </a></td><td align="center" valign="middle">
|
||||
<a href="https://www.novologic.com/" target="_blank"><img src="https://nestjs.com/img/novologic.png" width="110" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://ever.co/" target="_blank"><img src="https://nestjs.com/img/ever-logo.png" width="72" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://blokt.com" target="_blank"><img src="https://nestjs.com/img/blokt-logo.png" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="http://architectnow.net/" target="_blank"><img src="https://nestjs.com/img/architectnow.png" width="125" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://quander.io/" target="_blank"><img src="https://nestjs.com/img/quander.png" width="100" valign="middle" /></a> </td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://mantro.net/" target="_blank"><img src="https://nestjs.com/img/mantro-logo.svg" width="95" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://triplebyte.com/" target="_blank"><img src="https://nestjs.com/img/triplebyte.png" width="107" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://reposit.co.uk/" target="_blank"><img src="https://nestjs.com/img/reposit-logo.png" width="71" valign="middle" /></a></td><td align="center" valign="middle">
|
||||
<a href="https://nearpod.com/" target="_blank"><img src="https://nestjs.com/img/nearpod-logo.svg" width="100" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://clay.global/" target="_blank"><img src="https://nestjs.com/img/clay-logo.svg" width="75" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://firesticktricks.com" target="_blank"><img src="https://nestjs.com/img/firesticktricks-logo.png" width="120" valign="middle" /></a></td></tr><tr><td align="center" valign="middle">
|
||||
<a href="https://www.codeguesser.co.uk" target="_blank"><img src="https://nestjs.com/img/codeguesser-logo.svg" width="120" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://f-a.nz/" target="_blank"><img src="https://nestjs.com/img/franz.svg" width="80" valign="middle" /></a> </td><td align="center" valign="middle">
|
||||
<a href="https://sparkfabrik.com/" target="_blank"><img src="https://nestjs.com/img/sparkfabrik-logo.png" width="120" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://www.thebigphonestore.co.uk/" target="_blank"><img src="https://nestjs.com/img/the-big-phone-store-logo.png" width="65" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle">
|
||||
<a href="https://genuinebee.com/" target="_blank"><img src="https://nestjs.com/img/genuinebee.svg" width="97" valign="middle" /></a> </td>
|
||||
<td align="center" valign="middle"><a href="https://sanyodigital.com/" target="_blank"><img src="https://nestjs.com/img/sanyo-digital.png" width="130" valign="middle" /></a></td></tr><tr><td align="center" valign="middle"><a href="https://vpn-review.com/vpn-for-torrenting" target="_blank"><img src="https://nestjs.com/img/vpn-review-logo.png" width="85" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://lambda-it.ch/" target="_blank"><img src="https://nestjs.com/img/lambda-it-logo.svg" width="115" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://pickwriters.com/top-10-translation-services" target="_blank"><img src="https://nestjs.com/img/pickwriters-logo.png" width="40" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://thewordpoint.com/services/localization" target="_blank"><img src="https://nestjs.com/img/thewordpoint-logo.png" width="40" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://meercode.io/" target="_blank"><img src="https://nestjs.com/img/meercode-logo.png" width="60" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.najlepszeplatformyforex.pl/blog/broker-xtb/" target="_blank"><img src="https://nestjs.com/img/npf-logo.jpg" width="200" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://thestandarddaily.com/" target="_blank"><img src="https://nestjs.com/img/the-standard-daily-logo.png" width="180" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://houseofangular.io/" target="_blank"><img src="https://nestjs.com/img/house-of-angular.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://rocketech.it/cases/?utm_source=google&utm_medium=badge&utm_campaign=nestjs" target="_blank"><img src="https://nestjs.com/img/rocketech-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.bystored.com/" target="_blank"><img src="https://nestjs.com/img/stored-logo.svg" width="110" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://studyclerk.com/pay-for-research-paper" target="_blank"><img src="https://nestjs.com/img/studyclerk-logo.png" width="125" valign="middle" /></a></td><td align="center" valign="middle"><a href="https://xyndata.com" target="_blank"><img src="https://nestjs.com/img/xyndata-logo.png" width="125" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.anonymistic.com/" target="_blank"><img src="https://nestjs.com/img/anonymistic-logo.png" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.naologic.com/" target="_blank"><img src="https://nestjs.com/img/naologic-logo.svg" width="125" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://messaged.com/" target="_blank"><img src="https://nestjs.com/img/messaged-logo.png" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://triplecore.io" target="_blank"><img src="https://nestjs.com/img/triplecore-logo.svg" width="50" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://klqc.de" target="_blank"><img src="https://nestjs.com/img/klcqcl-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://thecasinowizard.com/bonuses/no-deposit-bonuses/" target="_blank"><img src="https://nestjs.com/img/casinowizard-logo.png" width="120" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://polygon-software.ch/" target="_blank"><img src="https://nestjs.com/img/polygon-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://themobilereality.com/" target="_blank"><img src="https://nestjs.com/img/mobile-reality-logo.png" width="45" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://boringowl.io/" target="_blank"><img src="https://nestjs.com/img/boringowl-logo.svg" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://www.mobilefactory.jp/" target="_blank"><img src="https://nestjs.com/img/mobilefactory-logo.png" width="100" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://nordbot.app/" target="_blank"><img src="https://nestjs.com/img/nordbot-logo.png" width="120" valign="middle" /></a></td>
|
||||
<td align="center" valign="middle"><a href="https://doppio.sh/" target="_blank"><img src="https://nestjs.com/img/dopiosh-logo.png" width="50" valign="middle" /></a></td></tr><tr>
|
||||
<td align="center" valign="middle"><a href="https://www.hingehealth.com/" target="_blank"><img src="https://nestjs.com/img/hinge-health-logo.svg" width="100" valign="middle" /></a></td>
|
||||
</tr></table>
|
||||
|
||||
## Backers
|
||||
|
||||
@@ -137,9 +146,9 @@ Nest is an MIT-licensed open source project. It can grow thanks to the sponsors
|
||||
|
||||
## Stay in touch
|
||||
|
||||
- Author - [Kamil Myśliwiec](https://x.com/kammysliwiec)
|
||||
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||
- X - [@nestframework](https://x.com/nestframework)
|
||||
* Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
|
||||
* Website - [https://nestjs.com](https://nestjs.com/)
|
||||
* Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||
|
||||
## License
|
||||
|
||||
|
||||
88
benchmarks/all_output.txt
Normal file
88
benchmarks/all_output.txt
Normal file
@@ -0,0 +1,88 @@
|
||||
-----------------------
|
||||
express
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬────────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼────────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 55 ms │ 58 ms │ 91 ms │ 138 ms │ 61.88 ms │ 23.95 ms │ 747 ms │
|
||||
└─────────┴───────┴───────┴───────┴────────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬──────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 8407 │ 8407 │ 17407 │ 17743 │ 16454.41 │ 2716.94 │ 8402 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼──────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 1.81 MB │ 1.81 MB │ 3.74 MB │ 3.81 MB │ 3.54 MB │ 584 kB │ 1.81 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴──────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
165k requests in 10.17s, 35.4 MB read
|
||||
-----------------------
|
||||
nest (with "@nestjs/platform-express")
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 61 ms │ 64 ms │ 71 ms │ 94 ms │ 65.44 ms │ 17.35 ms │ 325 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
|
||||
│ Req/Sec │ 14183 │ 14183 │ 15767 │ 15991 │ 15640 │ 501.13 │ 14182 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.06 MB │ 3.06 MB │ 3.41 MB │ 3.45 MB │ 3.38 MB │ 108 kB │ 3.06 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
156k requests in 10.24s, 33.8 MB read
|
||||
-----------------------
|
||||
fastify
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬─────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼─────────┤
|
||||
│ Latency │ 27 ms │ 30 ms │ 39 ms │ 78 ms │ 31.62 ms │ 26.59 ms │ 1232 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴─────────┘
|
||||
┌───────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 19935 │ 19935 │ 33247 │ 34111 │ 32030.4 │ 4103.84 │ 19931 │
|
||||
├───────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.03 MB │ 3.03 MB │ 5.05 MB │ 5.19 MB │ 4.87 MB │ 624 kB │ 3.03 MB │
|
||||
└───────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
320k requests in 10.18s, 48.7 MB read
|
||||
-----------------------
|
||||
nest (with "@nestjs/platform-fastify")
|
||||
-----------------------
|
||||
Running 10s test @ http://localhost:3000
|
||||
1024 connections
|
||||
|
||||
┌─────────┬───────┬───────┬───────┬───────┬──────────┬──────────┬────────┐
|
||||
│ Stat │ 2.5% │ 50% │ 97.5% │ 99% │ Avg │ Stdev │ Max │
|
||||
├─────────┼───────┼───────┼───────┼───────┼──────────┼──────────┼────────┤
|
||||
│ Latency │ 31 ms │ 33 ms │ 38 ms │ 52 ms │ 34.41 ms │ 11.73 ms │ 245 ms │
|
||||
└─────────┴───────┴───────┴───────┴───────┴──────────┴──────────┴────────┘
|
||||
┌───────────┬─────────┬─────────┬────────┬─────────┬─────────┬─────────┬─────────┐
|
||||
│ Stat │ 1% │ 2.5% │ 50% │ 97.5% │ Avg │ Stdev │ Min │
|
||||
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Req/Sec │ 24911 │ 24911 │ 30031 │ 30335 │ 29470.4 │ 1564.48 │ 24907 │
|
||||
├───────────┼─────────┼─────────┼────────┼─────────┼─────────┼─────────┼─────────┤
|
||||
│ Bytes/Sec │ 3.81 MB │ 3.81 MB │ 4.6 MB │ 4.64 MB │ 4.51 MB │ 239 kB │ 3.81 MB │
|
||||
└───────────┴─────────┴─────────┴────────┴─────────┴─────────┴─────────┴─────────┘
|
||||
|
||||
Req/Bytes counts sampled once per second.
|
||||
|
||||
295k requests in 10.17s, 45.1 MB read
|
||||
7
benchmarks/express.js
Normal file
7
benchmarks/express.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const express = require('express');
|
||||
const app = express();
|
||||
|
||||
app.get('/', async (req, res) => res.send('Hello world'));
|
||||
app.listen(3000);
|
||||
7
benchmarks/fastify.js
Normal file
7
benchmarks/fastify.js
Normal file
@@ -0,0 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const fastify = require('fastify')();
|
||||
fastify.get('/', async (req, reply) => reply.send('Hello world'));
|
||||
fastify.listen({
|
||||
port: 3000
|
||||
});
|
||||
14
benchmarks/nest-fastify.js
Normal file
14
benchmarks/nest-fastify.js
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const core_1 = require('@nestjs/core');
|
||||
const fastify_platform_1 = require('@nestjs/platform-fastify');
|
||||
const app_module_1 = require('./nest/app.module');
|
||||
core_1.NestFactory.create(
|
||||
app_module_1.AppModule,
|
||||
new fastify_platform_1.FastifyAdapter(),
|
||||
{
|
||||
logger: false,
|
||||
bodyParser: false,
|
||||
},
|
||||
).then(app => app.listen(3000));
|
||||
//# sourceMappingURL=main.js.map
|
||||
9
benchmarks/nest.js
Normal file
9
benchmarks/nest.js
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict';
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const core_1 = require('@nestjs/core');
|
||||
const app_module_1 = require('./nest/app.module');
|
||||
core_1.NestFactory.create(app_module_1.AppModule, {
|
||||
logger: false,
|
||||
bodyParser: false,
|
||||
}).then(app => app.listen(3000));
|
||||
//# sourceMappingURL=main.js.map
|
||||
3
benchmarks/nest/app.controller.d.ts
vendored
Normal file
3
benchmarks/nest/app.controller.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export declare class AppController {
|
||||
root(): string;
|
||||
}
|
||||
47
benchmarks/nest/app.controller.js
Normal file
47
benchmarks/nest/app.controller.js
Normal file
@@ -0,0 +1,47 @@
|
||||
'use strict';
|
||||
var __decorate =
|
||||
(this && this.__decorate) ||
|
||||
function(decorators, target, key, desc) {
|
||||
var c = arguments.length,
|
||||
r =
|
||||
c < 3
|
||||
? target
|
||||
: desc === null
|
||||
? (desc = Object.getOwnPropertyDescriptor(target, key))
|
||||
: desc,
|
||||
d;
|
||||
if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function')
|
||||
r = Reflect.decorate(decorators, target, key, desc);
|
||||
else
|
||||
for (var i = decorators.length - 1; i >= 0; i--)
|
||||
if ((d = decorators[i]))
|
||||
r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata =
|
||||
(this && this.__metadata) ||
|
||||
function(k, v) {
|
||||
if (typeof Reflect === 'object' && typeof Reflect.metadata === 'function')
|
||||
return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
const common_1 = require('@nestjs/common');
|
||||
let AppController = class AppController {
|
||||
root() {
|
||||
return 'Hello world!';
|
||||
}
|
||||
};
|
||||
__decorate(
|
||||
[
|
||||
common_1.Get(),
|
||||
__metadata('design:type', Function),
|
||||
__metadata('design:paramtypes', []),
|
||||
__metadata('design:returntype', String),
|
||||
],
|
||||
AppController.prototype,
|
||||
'root',
|
||||
null,
|
||||
);
|
||||
AppController = __decorate([common_1.Controller()], AppController);
|
||||
exports.AppController = AppController;
|
||||
//# sourceMappingURL=app.controller.js.map
|
||||
1
benchmarks/nest/app.controller.js.map
Normal file
1
benchmarks/nest/app.controller.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;AAAA,2CAAiD;AAGjD,IAAa,aAAa,GAA1B,MAAa,aAAa;IAExB,IAAI;QACF,OAAO,cAAc,CAAA;IACvB,CAAC;CACF,CAAA;AAHC;IADC,YAAG,EAAE;;;;yCAGL;AAJU,aAAa;IADzB,mBAAU,EAAE;GACA,aAAa,CAKzB;AALY,sCAAa"}
|
||||
1
benchmarks/nest/app.module.d.ts
vendored
Normal file
1
benchmarks/nest/app.module.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare class AppModule {}
|
||||
20
benchmarks/nest/app.module.js
Normal file
20
benchmarks/nest/app.module.js
Normal file
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const common_1 = require("@nestjs/common");
|
||||
const app_controller_1 = require("./app.controller");
|
||||
let AppModule = class AppModule {
|
||||
};
|
||||
AppModule = __decorate([
|
||||
common_1.Module({
|
||||
imports: [],
|
||||
controllers: [app_controller_1.AppController],
|
||||
})
|
||||
], AppModule);
|
||||
exports.AppModule = AppModule;
|
||||
//# sourceMappingURL=app.module.js.map
|
||||
1
benchmarks/nest/app.module.js.map
Normal file
1
benchmarks/nest/app.module.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;AAAA,2CAAwC;AACxC,qDAAiD;AAMjD,IAAa,SAAS,GAAtB,MAAa,SAAS;CAAG,CAAA;AAAZ,SAAS;IAJrB,eAAM,CAAC;QACN,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC,8BAAa,CAAC;KAC7B,CAAC;GACW,SAAS,CAAG;AAAZ,8BAAS"}
|
||||
1
benchmarks/nest/main.d.ts
vendored
Normal file
1
benchmarks/nest/main.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
1
benchmarks/nest/main.js.map
Normal file
1
benchmarks/nest/main.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAChD,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AACzB,CAAC;AACD,SAAS,EAAE,CAAC"}
|
||||
@@ -1,55 +0,0 @@
|
||||
// @ts-check
|
||||
import eslint from '@eslint/js';
|
||||
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';
|
||||
import globals from 'globals';
|
||||
import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ['node_modules', '**/node_modules/**', '**/*.js', '**/*.d.ts'],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
eslintPluginPrettierRecommended,
|
||||
{
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
...globals.jest,
|
||||
},
|
||||
ecmaVersion: 5,
|
||||
sourceType: 'module',
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'off',
|
||||
'@typescript-eslint/no-unsafe-call': 'off',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'off',
|
||||
'@typescript-eslint/no-unsafe-function-type': 'off',
|
||||
'@typescript-eslint/no-unsafe-argument': 'off',
|
||||
'@typescript-eslint/no-unsafe-return': 'off',
|
||||
'@typescript-eslint/no-unused-expressions': 'off',
|
||||
'@typescript-eslint/no-require-imports': 'off',
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'@typescript-eslint/no-non-null-asserted-optional-chain': 'warn',
|
||||
'@typescript-eslint/no-misused-promises': [
|
||||
'error',
|
||||
{
|
||||
checksVoidReturn: false,
|
||||
checksConditionals: false,
|
||||
},
|
||||
],
|
||||
'@typescript-eslint/require-await': 'off',
|
||||
'@typescript-eslint/prefer-promise-reject-errors': 'off',
|
||||
'@typescript-eslint/no-base-to-string': 'off',
|
||||
'@typescript-eslint/unbound-method': 'off',
|
||||
'@typescript-eslint/only-throw-error': 'off',
|
||||
},
|
||||
},
|
||||
);
|
||||
@@ -4,7 +4,6 @@ import * as chaiAsPromised from 'chai-as-promised';
|
||||
import * as sinon from 'sinon';
|
||||
import { BarService } from '../src/bar.service';
|
||||
import { FooService } from '../src/foo.service';
|
||||
|
||||
chai.use(chaiAsPromised);
|
||||
const { expect } = chai;
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
@@ -33,8 +32,7 @@
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*",
|
||||
"test/**/*"
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
|
||||
@@ -38,12 +38,10 @@ describe.skip('Fastify Cors', () => {
|
||||
);
|
||||
|
||||
let requestId = 0;
|
||||
const configDelegation = {
|
||||
delegator: function (req, cb) {
|
||||
const config = configs[requestId];
|
||||
requestId++;
|
||||
cb(null, config);
|
||||
},
|
||||
const configDelegation = function (req, cb) {
|
||||
const config = configs[requestId];
|
||||
requestId++;
|
||||
cb(null, config);
|
||||
};
|
||||
app.enableCors(configDelegation);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { WebhooksExplorer } from '../src/webhooks.explorer';
|
||||
import { NonAppliedDecorator } from '../src/decorators/non-applied.decorator';
|
||||
|
||||
describe('DiscoveryModule', () => {
|
||||
let moduleRef: TestingModule;
|
||||
|
||||
beforeEach(async () => {
|
||||
moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
});
|
||||
|
||||
it('should discover all providers & handlers with corresponding annotations', async () => {
|
||||
const webhooksExplorer = moduleRef.get(WebhooksExplorer);
|
||||
|
||||
expect(webhooksExplorer.getWebhooks()).to.be.eql([
|
||||
{
|
||||
handlers: [
|
||||
{
|
||||
event: 'start',
|
||||
methodName: 'onStart',
|
||||
},
|
||||
],
|
||||
name: 'cleanup',
|
||||
},
|
||||
{
|
||||
handlers: [
|
||||
{
|
||||
event: 'start',
|
||||
methodName: 'onStart',
|
||||
},
|
||||
],
|
||||
name: 'flush',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return an empty array if no providers were found for a given discoverable decorator', () => {
|
||||
const discoveryService = moduleRef.get(DiscoveryService);
|
||||
|
||||
const providers = discoveryService.getProviders({
|
||||
metadataKey: NonAppliedDecorator.KEY,
|
||||
});
|
||||
expect(providers).to.be.eql([]);
|
||||
});
|
||||
|
||||
it('should return an empty array if no controllers were found for a given discoverable decorator', () => {
|
||||
const discoveryService = moduleRef.get(DiscoveryService);
|
||||
|
||||
const controllers = discoveryService.getControllers({
|
||||
metadataKey: NonAppliedDecorator.KEY,
|
||||
});
|
||||
expect(controllers).to.be.eql([]);
|
||||
});
|
||||
});
|
||||
@@ -1,10 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { DiscoveryModule } from '@nestjs/core';
|
||||
import { MyWebhookModule } from './my-webhook/my-webhook.module';
|
||||
import { WebhooksExplorer } from './webhooks.explorer';
|
||||
|
||||
@Module({
|
||||
imports: [MyWebhookModule, DiscoveryModule],
|
||||
providers: [WebhooksExplorer],
|
||||
})
|
||||
export class AppModule {}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
|
||||
/**
|
||||
* This decorator must not be used anywhere!
|
||||
*
|
||||
* This will be used to test the scenario where we are trying to retrieving
|
||||
* metadata for a discoverable decorator that was not applied to any class.
|
||||
*/
|
||||
export const NonAppliedDecorator = DiscoveryService.createDecorator();
|
||||
@@ -1,6 +0,0 @@
|
||||
import { DiscoveryService } from '@nestjs/core';
|
||||
|
||||
export const Webhook = DiscoveryService.createDecorator<{ name: string }>();
|
||||
export const WebhookHandler = DiscoveryService.createDecorator<{
|
||||
event: string;
|
||||
}>();
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
|
||||
@Webhook({ name: 'cleanup' })
|
||||
export class CleanupWebhook {
|
||||
@WebhookHandler({ event: 'start' })
|
||||
onStart() {
|
||||
console.log('cleanup started');
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
import { Webhook, WebhookHandler } from '../decorators/webhook.decorators';
|
||||
|
||||
@Webhook({ name: 'flush' })
|
||||
export class FlushWebhook {
|
||||
@WebhookHandler({ event: 'start' })
|
||||
onStart() {
|
||||
console.log('flush started');
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CleanupWebhook } from './cleanup.webhook';
|
||||
import { FlushWebhook } from './flush.webhook';
|
||||
|
||||
@Module({ providers: [CleanupWebhook, FlushWebhook] })
|
||||
export class MyWebhookModule {}
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { DiscoveryService, MetadataScanner } from '@nestjs/core';
|
||||
import { Webhook, WebhookHandler } from './decorators/webhook.decorators';
|
||||
|
||||
@Injectable()
|
||||
export class WebhooksExplorer {
|
||||
constructor(
|
||||
private readonly discoveryService: DiscoveryService,
|
||||
private readonly metadataScanner: MetadataScanner,
|
||||
) {}
|
||||
|
||||
getWebhooks() {
|
||||
const webhooks = this.discoveryService.getProviders({
|
||||
metadataKey: Webhook.KEY,
|
||||
});
|
||||
return webhooks.map(wrapper => {
|
||||
const { name } = this.discoveryService.getMetadataByDecorator(
|
||||
Webhook,
|
||||
wrapper,
|
||||
)!;
|
||||
return {
|
||||
name,
|
||||
handlers: this.metadataScanner
|
||||
.getAllMethodNames(wrapper.metatype!.prototype)
|
||||
.map(methodName => {
|
||||
const { event } = this.discoveryService.getMetadataByDecorator(
|
||||
WebhookHandler,
|
||||
wrapper,
|
||||
methodName,
|
||||
)!;
|
||||
return {
|
||||
methodName,
|
||||
event,
|
||||
};
|
||||
}),
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"declaration": false,
|
||||
"noImplicitAny": false,
|
||||
"removeComments": true,
|
||||
"noLib": false,
|
||||
"emitDecoratorMetadata": true,
|
||||
"experimentalDecorators": true,
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
"@nestjs/common/*": ["../../packages/common/*"],
|
||||
"@nestjs/core": ["../../packages/core"],
|
||||
"@nestjs/core/*": ["../../packages/core/*"],
|
||||
"@nestjs/microservices": ["../../packages/microservices"],
|
||||
"@nestjs/microservices/*": ["../../packages/microservices/*"],
|
||||
"@nestjs/websockets": ["../../packages/websockets"],
|
||||
"@nestjs/websockets/*": ["../../packages/websockets/*"],
|
||||
"@nestjs/testing": ["../../packages/testing"],
|
||||
"@nestjs/testing/*": ["../../packages/testing/*"],
|
||||
"@nestjs/platform-express": ["../../packages/platform-express"],
|
||||
"@nestjs/platform-express/*": ["../../packages/platform-express/*"],
|
||||
"@nestjs/platform-socket.io": ["../../packages/platform-socket.io"],
|
||||
"@nestjs/platform-socket.io/*": ["../../packages/platform-socket.io/*"],
|
||||
"@nestjs/platform-ws": ["../../packages/platform-ws"],
|
||||
"@nestjs/platform-ws/*": ["../../packages/platform-ws/*"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*",
|
||||
"e2e/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"node_modules",
|
||||
]
|
||||
}
|
||||
@@ -25,9 +25,8 @@ services:
|
||||
- "9001:9001"
|
||||
restart: always
|
||||
mysql:
|
||||
image: mysql:9.6.0
|
||||
image: mysql:8.0.32
|
||||
environment:
|
||||
MYSQL_ROOT_HOST: '%'
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: test
|
||||
ports:
|
||||
@@ -51,7 +50,7 @@ services:
|
||||
zookeeper:
|
||||
container_name: test-zookeeper
|
||||
hostname: zookeeper
|
||||
image: confluentinc/cp-zookeeper:7.9.5
|
||||
image: confluentinc/cp-zookeeper:7.3.2
|
||||
ports:
|
||||
- "2181:2181"
|
||||
environment:
|
||||
@@ -60,7 +59,7 @@ services:
|
||||
kafka:
|
||||
container_name: test-kafka
|
||||
hostname: kafka
|
||||
image: confluentinc/cp-kafka:8.1.1
|
||||
image: confluentinc/cp-kafka:7.3.2
|
||||
depends_on:
|
||||
- zookeeper
|
||||
ports:
|
||||
|
||||
@@ -7,4 +7,4 @@ async function bootstrap() {
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
await app.listen(3000);
|
||||
}
|
||||
void bootstrap();
|
||||
bootstrap();
|
||||
|
||||
@@ -35,7 +35,7 @@ export class RecipesResolver {
|
||||
@Args('newRecipeData') newRecipeData: NewRecipeInput,
|
||||
): Promise<Recipe> {
|
||||
const recipe = await this.recipesService.create(newRecipeData);
|
||||
void pubSub.publish('recipeAdded', { recipeAdded: recipe });
|
||||
pubSub.publish('recipeAdded', { recipeAdded: recipe });
|
||||
return recipe;
|
||||
}
|
||||
|
||||
@@ -46,6 +46,6 @@ export class RecipesResolver {
|
||||
|
||||
@Subscription(returns => Recipe)
|
||||
recipeAdded() {
|
||||
return pubSub.asyncIterableIterator('recipeAdded');
|
||||
return pubSub.asyncIterator('recipeAdded');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -42,7 +42,7 @@ describe('GraphQL request scoped', () => {
|
||||
],
|
||||
},
|
||||
})
|
||||
.end(err => {
|
||||
.end((err, res) => {
|
||||
if (err) return end(err);
|
||||
end();
|
||||
});
|
||||
|
||||
@@ -20,6 +20,6 @@ export class CatsRequestScopedService {
|
||||
}
|
||||
|
||||
findOneById(id: number): Cat {
|
||||
return this.cats.find(cat => cat.id === id)!;
|
||||
return this.cats.find(cat => cat.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,13 +27,13 @@ export class CatsResolvers {
|
||||
|
||||
@Mutation('createCat')
|
||||
async create(@Args() args: Cat): Promise<Cat> {
|
||||
const createdCat = this.catsService.create(args);
|
||||
void pubSub.publish('catCreated', { catCreated: createdCat });
|
||||
const createdCat = await this.catsService.create(args);
|
||||
pubSub.publish('catCreated', { catCreated: createdCat });
|
||||
return createdCat;
|
||||
}
|
||||
|
||||
@Subscription('catCreated')
|
||||
catCreated() {
|
||||
return pubSub.asyncIterableIterator('catCreated');
|
||||
return pubSub.asyncIterator('catCreated');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,6 @@ export class CatsService {
|
||||
}
|
||||
|
||||
findOneById(id: number): Cat {
|
||||
return this.cats.find(cat => cat.id === id)!;
|
||||
return this.cats.find(cat => cat.id === id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(3000);
|
||||
}
|
||||
void bootstrap();
|
||||
bootstrap();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -50,16 +50,6 @@ class TestController {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('legacy-wildcard/overview')
|
||||
testLegacyWildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('splat-wildcard/overview')
|
||||
testSplatWildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('overview/:id')
|
||||
overviewById() {
|
||||
return RETURN_VALUE;
|
||||
@@ -74,17 +64,10 @@ class TestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.end(MIDDLEWARE_VALUE))
|
||||
.exclude(
|
||||
'test',
|
||||
'overview/:id',
|
||||
'wildcard/*',
|
||||
'legacy-wildcard/(.*)',
|
||||
'splat-wildcard/*splat',
|
||||
{
|
||||
path: 'middleware',
|
||||
method: RequestMethod.POST,
|
||||
},
|
||||
)
|
||||
.exclude('test', 'overview/:id', 'wildcard/(.*)', {
|
||||
path: 'middleware',
|
||||
method: RequestMethod.POST,
|
||||
})
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
@@ -143,18 +126,6 @@ describe('Exclude middleware (fastify)', () => {
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/legacy-wildcard/overview" endpoint (by wildcard, legacy syntax)`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/legacy-wildcard/overview')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/splat-wildcard/overview" endpoint (by wildcard, new syntax)`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/splat-wildcard/overview')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -41,25 +41,10 @@ class TestController {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('legacy-wildcard/overview')
|
||||
testLegacyWildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('splat-wildcard/overview')
|
||||
testSplatWildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('overview/:id')
|
||||
overviewById() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('multiple/exclude')
|
||||
multipleExclude() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -70,19 +55,11 @@ class TestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(MIDDLEWARE_VALUE))
|
||||
.exclude(
|
||||
'test',
|
||||
'overview/:id',
|
||||
'wildcard/*',
|
||||
'legacy-wildcard/(.*)',
|
||||
'splat-wildcard/*splat',
|
||||
{
|
||||
path: 'middleware',
|
||||
method: RequestMethod.POST,
|
||||
},
|
||||
)
|
||||
.exclude('multiple/exclude')
|
||||
.forRoutes('*path');
|
||||
.exclude('test', 'overview/:id', 'wildcard/(.*)', {
|
||||
path: 'middleware',
|
||||
method: RequestMethod.POST,
|
||||
})
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,24 +110,6 @@ describe('Exclude middleware', () => {
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/legacy-wildcard/overview" endpoint (by wildcard, legacy syntax)`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/legacy-wildcard/overview')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/splat-wildcard/overview" endpoint (by wildcard, new syntax)`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/splat-wildcard/overview')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
it(`should exclude "/multiple/exclude" endpoint`, () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/multiple/exclude')
|
||||
.expect(200, RETURN_VALUE);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -3,11 +3,10 @@ import { ExpressAdapter } from '@nestjs/platform-express';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as express from 'express';
|
||||
import * as request from 'supertest';
|
||||
import { App } from 'supertest/types';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Hello world (express instance)', () => {
|
||||
let server: App;
|
||||
let server;
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
@@ -46,10 +45,6 @@ describe('Hello world (express instance)', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('/HEAD should respond to with a 200', () => {
|
||||
return request(server).head('/hello').expect(200);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -87,15 +87,6 @@ describe('Hello world (fastify adapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql('Hello world!'));
|
||||
});
|
||||
|
||||
it('/HEAD should respond to with a 200', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'HEAD',
|
||||
url: '/hello',
|
||||
})
|
||||
.then(({ statusCode }) => expect(statusCode).to.be.eq(200));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Injectable,
|
||||
MiddlewareConsumer,
|
||||
Module,
|
||||
NestModule,
|
||||
} from '@nestjs/common';
|
||||
import {
|
||||
FastifyAdapter,
|
||||
NestFastifyApplication,
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Middleware before init (FastifyAdapter)', () => {
|
||||
let app: NestFastifyApplication;
|
||||
|
||||
@Injectable()
|
||||
class TestService {
|
||||
getData(): string {
|
||||
return 'test_data';
|
||||
}
|
||||
}
|
||||
|
||||
@Controller()
|
||||
class TestController {
|
||||
constructor(private readonly testService: TestService) {}
|
||||
|
||||
@Get('test')
|
||||
test() {
|
||||
return { data: this.testService.getData() };
|
||||
}
|
||||
|
||||
@Get('health')
|
||||
health() {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
controllers: [TestController],
|
||||
providers: [TestService],
|
||||
})
|
||||
class TestModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => {
|
||||
res.setHeader('x-middleware', 'applied');
|
||||
next();
|
||||
})
|
||||
.forRoutes('*');
|
||||
}
|
||||
}
|
||||
|
||||
describe('should queue middleware when registered before init', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
|
||||
// Register middleware before init - should be queued
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('x-global-middleware', 'applied');
|
||||
next();
|
||||
});
|
||||
|
||||
// Now init the app - queued middleware should be registered
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
it('should apply queued middleware after init', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload, headers }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
// Verify both module-level and global middleware were applied
|
||||
expect(headers['x-middleware']).to.equal('applied');
|
||||
expect(headers['x-global-middleware']).to.equal('applied');
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should work when app is initialized before middleware registration', () => {
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter(),
|
||||
);
|
||||
|
||||
// Initialize app first
|
||||
await app.init();
|
||||
|
||||
// Now middleware registration should work
|
||||
app.use((req, res, next) => {
|
||||
res.setHeader('x-global-middleware', 'applied');
|
||||
next();
|
||||
});
|
||||
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
});
|
||||
|
||||
it('should register middleware successfully after init', () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/test',
|
||||
})
|
||||
.then(({ statusCode, payload }) => {
|
||||
expect(statusCode).to.equal(200);
|
||||
expect(JSON.parse(payload)).to.deep.equal({ data: 'test_data' });
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,163 +0,0 @@
|
||||
import { ConsoleLogger, INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import * as sinon from 'sinon';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('ForceConsole Option', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
describe('When forceConsole is true', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication({
|
||||
forceConsole: true,
|
||||
logger: ['log', 'error'],
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should use console.log instead of process.stdout.write', async () => {
|
||||
const logger = new ConsoleLogger('TestContext', { forceConsole: true });
|
||||
logger.log('Test log message');
|
||||
|
||||
// Should use console.log when forceConsole is true
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
// Verify console.log was called with the message
|
||||
const consoleLogCalls = consoleLogSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test log message')),
|
||||
);
|
||||
expect(consoleLogCalls.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should use console.error instead of process.stderr.write', async () => {
|
||||
const logger = new ConsoleLogger('TestContext', { forceConsole: true });
|
||||
logger.error('Test error message');
|
||||
|
||||
// Should use console.error when forceConsole is true
|
||||
expect(consoleErrorSpy.called).to.be.true;
|
||||
// Verify console.error was called with the message
|
||||
const consoleErrorCalls = consoleErrorSpy
|
||||
.getCalls()
|
||||
.filter(call =>
|
||||
call.args.some(arg => String(arg).includes('Test error message')),
|
||||
);
|
||||
expect(consoleErrorCalls.length).to.be.greaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle GET request with forceConsole option enabled', () => {
|
||||
return request(app.getHttpServer()).get('/hello').expect(200);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forceConsole is false (default)', () => {
|
||||
let consoleLogSpy: sinon.SinonSpy;
|
||||
let consoleErrorSpy: sinon.SinonSpy;
|
||||
let processStdoutSpy: sinon.SinonSpy;
|
||||
let processStderrSpy: sinon.SinonSpy;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Spy on console and process methods
|
||||
consoleLogSpy = sinon.spy(console, 'log');
|
||||
consoleErrorSpy = sinon.spy(console, 'error');
|
||||
processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
processStderrSpy = sinon.spy(process.stderr, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = moduleRef.createNestApplication({
|
||||
logger: ['log', 'error'],
|
||||
// forceConsole is not set, defaults to false
|
||||
});
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
consoleLogSpy.restore();
|
||||
consoleErrorSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
processStderrSpy.restore();
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should not directly call console.log when forceConsole is false', async () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleLogSpy.resetHistory();
|
||||
|
||||
logger.log('Test log message');
|
||||
|
||||
// When forceConsole is false, should not call console.log
|
||||
expect(consoleLogSpy.called).to.be.false;
|
||||
});
|
||||
|
||||
it('should not directly call console.error when forceConsole is false', async () => {
|
||||
const logger = new ConsoleLogger('TestContext');
|
||||
|
||||
// Reset spy to ensure clean state
|
||||
consoleErrorSpy.resetHistory();
|
||||
|
||||
logger.error('Test error message');
|
||||
|
||||
// When forceConsole is false, should not call console.error
|
||||
expect(consoleErrorSpy.called).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('When forceConsole is set via NestFactory.create', () => {
|
||||
it('should apply forceConsole to the default logger', async () => {
|
||||
const consoleLogSpy = sinon.spy(console, 'log');
|
||||
const processStdoutSpy = sinon.spy(process.stdout, 'write');
|
||||
|
||||
const moduleRef = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
const testApp = moduleRef.createNestApplication({
|
||||
forceConsole: true,
|
||||
});
|
||||
|
||||
await testApp.init();
|
||||
|
||||
// The logger created by NestFactory should respect forceConsole option
|
||||
const logger = new ConsoleLogger('AppContext', { forceConsole: true });
|
||||
logger.log('Application started');
|
||||
|
||||
expect(consoleLogSpy.called).to.be.true;
|
||||
|
||||
consoleLogSpy.restore();
|
||||
processStdoutSpy.restore();
|
||||
await testApp.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -48,7 +48,7 @@ export class HeaderInterceptor {
|
||||
const ctx = context.switchToHttp();
|
||||
const res = ctx.getResponse();
|
||||
for (const key in this.headers) {
|
||||
if (Object.prototype.hasOwnProperty.call(this.headers, key)) {
|
||||
if (this.headers.hasOwnProperty(key)) {
|
||||
res.header(key, this.headers[key]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
RequestMethod,
|
||||
} from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { Response } from 'express';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import { Response } from 'express';
|
||||
|
||||
const INCLUDED_VALUE = 'test_included';
|
||||
const RETURN_VALUE = 'test';
|
||||
|
||||
@@ -1,56 +1,20 @@
|
||||
import {
|
||||
Global,
|
||||
INestApplication,
|
||||
MiddlewareConsumer,
|
||||
Module,
|
||||
} from '@nestjs/common';
|
||||
import { INestApplication, MiddlewareConsumer, Module } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
|
||||
const RETURN_VALUE_A = 'test_A';
|
||||
const RETURN_VALUE_B = 'test_B';
|
||||
const RETURN_VALUE_X = 'test_X';
|
||||
const RETURN_VALUE_GLOBAL = 'test_GLOBAL';
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
class GlobalModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_GLOBAL))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
class GlobalModule2 {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_GLOBAL + '2'))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({ imports: [GlobalModule, GlobalModule2] })
|
||||
class ModuleX {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_X))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_X))
|
||||
.forRoutes('ping');
|
||||
}
|
||||
}
|
||||
|
||||
@Module({ imports: [ModuleX] })
|
||||
@Module({
|
||||
imports: [],
|
||||
})
|
||||
class ModuleA {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_A))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_A))
|
||||
.forRoutes('ping');
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_A);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,10 +24,10 @@ class ModuleA {
|
||||
class ModuleB {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_B))
|
||||
.forRoutes('hello')
|
||||
.apply((req, res, next) => res.send(RETURN_VALUE_B))
|
||||
.forRoutes('ping');
|
||||
.apply((req, res, next) => {
|
||||
res.send(RETURN_VALUE_B);
|
||||
})
|
||||
.forRoutes('hello');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,12 +55,6 @@ describe('Middleware (execution order)', () => {
|
||||
.expect(200, RETURN_VALUE_B);
|
||||
});
|
||||
|
||||
it('should execute global middleware first', () => {
|
||||
return request(app.getHttpServer())
|
||||
.get('/ping')
|
||||
.expect(200, RETURN_VALUE_GLOBAL);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
Module,
|
||||
NestMiddleware,
|
||||
NestModule,
|
||||
Param,
|
||||
Query,
|
||||
Req,
|
||||
RequestMethod,
|
||||
@@ -16,8 +15,6 @@ import {
|
||||
} from '@nestjs/platform-fastify';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import { FastifyRequest } from 'fastify';
|
||||
import * as request from 'supertest';
|
||||
import { AppModule } from '../src/app.module';
|
||||
|
||||
describe('Middleware (FastifyAdapter)', () => {
|
||||
@@ -38,11 +35,6 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('legacy_style_wildcard/wildcard_nested')
|
||||
legacy_style_wildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('test')
|
||||
test() {
|
||||
return RETURN_VALUE;
|
||||
@@ -82,13 +74,9 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.apply((req, res, next) => res.end(INCLUDED_VALUE))
|
||||
.forRoutes({ path: 'tests/included', method: RequestMethod.POST })
|
||||
.apply((req, res, next) => res.end(REQ_URL_VALUE))
|
||||
.forRoutes('req/url/*')
|
||||
.forRoutes('req/url/(.*)')
|
||||
.apply((req, res, next) => res.end(WILDCARD_VALUE))
|
||||
.forRoutes(
|
||||
'express_style_wildcard/*',
|
||||
'tests/*path',
|
||||
'legacy_style_wildcard/(.*)',
|
||||
)
|
||||
.forRoutes('express_style_wildcard/*', 'tests/(.*)')
|
||||
.apply((req, res, next) => res.end(QUERY_VALUE))
|
||||
.forRoutes('query')
|
||||
.apply((req, res, next) => next())
|
||||
@@ -97,7 +85,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.forRoutes(TestController)
|
||||
.apply((req, res, next) => res.end(RETURN_VALUE))
|
||||
.exclude({ path: QUERY_VALUE, method: -1 as any })
|
||||
.forRoutes('*');
|
||||
.forRoutes('(.*)');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -111,7 +99,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`forRoutes(*)`, () => {
|
||||
it(`forRoutes((.*))`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
@@ -153,7 +141,7 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql(QUERY_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(tests/*path)`, () => {
|
||||
it(`forRoutes(tests/(.*))`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
@@ -171,15 +159,6 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(legacy_style_wildcard/*)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/legacy_style_wildcard/wildcard_nested',
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(WILDCARD_VALUE));
|
||||
});
|
||||
|
||||
it(`forRoutes(req/url/)`, () => {
|
||||
const reqUrl = '/test';
|
||||
return app
|
||||
@@ -208,21 +187,12 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
it(`GET forRoutes(POST /tests/%69ncluded) - ensure middleware is executed correctly with encoded characters`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'POST',
|
||||
url: '/tests/%69ncluded', // 'i' character is encoded
|
||||
})
|
||||
.then(({ payload }) => expect(payload).to.be.eql(INCLUDED_VALUE));
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should execute middleware only once for given routes', () => {
|
||||
describe.only('should execute middleware only once for given routes', () => {
|
||||
class Middleware implements NestMiddleware {
|
||||
use(request: any, reply: any, next: () => void) {
|
||||
if (request.middlewareExecutionCount === undefined) {
|
||||
@@ -428,342 +398,4 @@ describe('Middleware (FastifyAdapter)', () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should have data attached in middleware', () => {
|
||||
@Controller()
|
||||
class DataController {
|
||||
@Get('data')
|
||||
async data(@Req() req: FastifyRequest['raw']) {
|
||||
return {
|
||||
success: true,
|
||||
extras: req?.['raw']?.extras,
|
||||
pong: req?.['raw']?.headers?.ping,
|
||||
};
|
||||
}
|
||||
@Get('pong')
|
||||
async pong(@Req() req: FastifyRequest['raw']) {
|
||||
return { success: true, pong: req?.['raw']?.headers?.ping };
|
||||
}
|
||||
|
||||
@Get('')
|
||||
async rootPath(@Req() req: FastifyRequest['raw']) {
|
||||
return { success: true, root: true };
|
||||
}
|
||||
|
||||
@Get('record/:id')
|
||||
async record(@Req() req: FastifyRequest['raw'], @Param('id') id: string) {
|
||||
return { success: true, record: id };
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
controllers: [DataController],
|
||||
})
|
||||
class DataModule implements NestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => {
|
||||
req.extras = { data: 'Data attached in middleware' };
|
||||
req.headers['ping'] = 'pong';
|
||||
|
||||
// When global prefix is set and the route is the root path
|
||||
if (req.originalUrl === '/api') {
|
||||
return res.end(JSON.stringify({ success: true, pong: 'pong' }));
|
||||
}
|
||||
next();
|
||||
})
|
||||
.forRoutes('{*path}');
|
||||
}
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [DataModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(new FastifyAdapter());
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix (route: /api/pong)`, async () => {
|
||||
app.setGlobalPrefix('/api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/api/pong',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix (route: /api)`, async () => {
|
||||
app.setGlobalPrefix('/api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/api',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') without prefix config`, async () => {
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/pong',
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(
|
||||
JSON.stringify({
|
||||
success: true,
|
||||
pong: 'pong',
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude patterns`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200, { success: true, root: true });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude pattern with wildcard`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/record/{*path}'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/pong')
|
||||
.expect(200, { success: true, pong: 'pong' });
|
||||
await request(app.getHttpServer())
|
||||
.get('/record/abc123')
|
||||
.expect(200, { success: true, record: 'abc123' });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and exclude pattern with parameter`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/record/:id'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/record/abc123')
|
||||
.expect(200, { success: true, record: 'abc123' });
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/pong')
|
||||
.expect(200, { success: true, pong: 'pong' });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix and global prefix options`, async () => {
|
||||
app.setGlobalPrefix('/api', { exclude: ['/'] });
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/data')
|
||||
.expect(200, {
|
||||
success: true,
|
||||
extras: { data: 'Data attached in middleware' },
|
||||
pong: 'pong',
|
||||
});
|
||||
await request(app.getHttpServer())
|
||||
.get('/')
|
||||
.expect(200, { success: true, root: true });
|
||||
});
|
||||
|
||||
it(`GET forRoutes('{*path}') with global prefix that not starts with /`, async () => {
|
||||
app.setGlobalPrefix('api');
|
||||
await app.init();
|
||||
await app.getHttpAdapter().getInstance().ready();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get('/api/data')
|
||||
.expect(200, {
|
||||
success: true,
|
||||
extras: { data: 'Data attached in middleware' },
|
||||
pong: 'pong',
|
||||
});
|
||||
await request(app.getHttpServer()).get('/').expect(404);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('should respect fastify routing options', () => {
|
||||
const MIDDLEWARE_RETURN_VALUE = 'middleware_return';
|
||||
|
||||
@Controller()
|
||||
class TestController {
|
||||
@Get('abc/def')
|
||||
included() {
|
||||
return 'whatnot';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [AppModule],
|
||||
controllers: [TestController],
|
||||
})
|
||||
class TestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.end(MIDDLEWARE_RETURN_VALUE))
|
||||
.forRoutes({ path: 'abc/def', method: RequestMethod.GET });
|
||||
}
|
||||
}
|
||||
|
||||
describe('[ignoreTrailingSlash] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
ignoreTrailingSlash: true,
|
||||
// routerOptions: {
|
||||
// ignoreTrailingSlash: true,
|
||||
// },
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc/def/)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc/def/', // trailing slash
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[ignoreDuplicateSlashes] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: {
|
||||
ignoreDuplicateSlashes: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc//def)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc//def', // duplicate slashes
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[caseSensitive] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: {
|
||||
caseSensitive: true,
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /ABC/DEF)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/ABC/DEF', // different case
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('[useSemicolonDelimiter] attribute', () => {
|
||||
beforeEach(async () => {
|
||||
app = (
|
||||
await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile()
|
||||
).createNestApplication<NestFastifyApplication>(
|
||||
new FastifyAdapter({
|
||||
routerOptions: { useSemicolonDelimiter: true } as any,
|
||||
}),
|
||||
);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it(`GET forRoutes(GET /abc/def;foo=bar)`, () => {
|
||||
return app
|
||||
.inject({
|
||||
method: 'GET',
|
||||
url: '/abc/def;foo=bar', // semicolon delimiter
|
||||
})
|
||||
.then(({ payload }) =>
|
||||
expect(payload).to.be.eql(MIDDLEWARE_RETURN_VALUE),
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -26,11 +26,6 @@ class TestController {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('legacy-wildcard/overview')
|
||||
legacyWildcard() {
|
||||
return RETURN_VALUE;
|
||||
}
|
||||
|
||||
@Get('exclude')
|
||||
exclude() {
|
||||
return EXCLUDE_VALUE;
|
||||
@@ -45,7 +40,7 @@ class TestModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
consumer
|
||||
.apply((req, res, next) => res.send(WILDCARD_VALUE))
|
||||
.forRoutes('tests/*path', 'legacy-wildcard/*')
|
||||
.forRoutes('tests/*')
|
||||
.apply((req, res, next) => res.send(SCOPED_VALUE))
|
||||
.exclude('exclude')
|
||||
.forRoutes(TestController)
|
||||
@@ -91,13 +86,6 @@ describe('Middleware', () => {
|
||||
.expect(200, WILDCARD_VALUE);
|
||||
});
|
||||
|
||||
it(`forRoutes(legacy-wildcard/*)`, async () => {
|
||||
app = await createApp();
|
||||
return request(app.getHttpServer())
|
||||
.get('/legacy-wildcard/overview')
|
||||
.expect(200, WILDCARD_VALUE);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ export class HostArrayController {
|
||||
|
||||
@Get('async')
|
||||
async asyncGreeting(@HostParam('tenant') tenant: string): Promise<string> {
|
||||
return `${this.hostService.greeting()} tenant=${tenant}`;
|
||||
return `${await this.hostService.greeting()} tenant=${tenant}`;
|
||||
}
|
||||
|
||||
@Get('stream')
|
||||
|
||||
@@ -18,7 +18,7 @@ export class HostController {
|
||||
|
||||
@Get('async')
|
||||
async asyncGreeting(@HostParam('tenant') tenant: string): Promise<string> {
|
||||
return `${this.hostService.greeting()} tenant=${tenant}`;
|
||||
return `${await this.hostService.greeting()} tenant=${tenant}`;
|
||||
}
|
||||
|
||||
@Get('stream')
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -23,7 +23,10 @@ describe('BeforeApplicationShutdown', () => {
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements BeforeApplicationShutdown {
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
public field: string;
|
||||
async beforeApplicationShutdown() {
|
||||
this.field = 'b-field';
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -34,8 +37,12 @@ describe('BeforeApplicationShutdown', () => {
|
||||
|
||||
@Injectable()
|
||||
class AA implements BeforeApplicationShutdown {
|
||||
public field: string;
|
||||
constructor(private bb: BB) {}
|
||||
beforeApplicationShutdown = Sinon.spy();
|
||||
|
||||
async beforeApplicationShutdown() {
|
||||
this.field = this.bb.field + '_a-field';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [B],
|
||||
@@ -51,11 +58,7 @@ describe('BeforeApplicationShutdown', () => {
|
||||
await app.init();
|
||||
await app.close();
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(
|
||||
aa.beforeApplicationShutdown,
|
||||
bb.beforeApplicationShutdown,
|
||||
);
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -51,21 +51,4 @@ describe('enableShutdownHooks', () => {
|
||||
expect(result.stdout.toString().trim()).to.be.eq('');
|
||||
done();
|
||||
}).timeout(10000);
|
||||
|
||||
it('should call the correct hooks with useProcessExit option', done => {
|
||||
const result = spawnSync('ts-node', [
|
||||
join(__dirname, '../src/enable-shutdown-hooks-main.ts'),
|
||||
'SIGHUP',
|
||||
'SIGHUP',
|
||||
'graceful',
|
||||
]);
|
||||
const calls = result.stdout
|
||||
.toString()
|
||||
.split('\n')
|
||||
.map((call: string) => call.trim());
|
||||
expect(calls[0]).to.equal('beforeApplicationShutdown SIGHUP');
|
||||
expect(calls[1]).to.equal('onApplicationShutdown SIGHUP');
|
||||
expect(result.status).to.equal(0);
|
||||
done();
|
||||
}).timeout(10000);
|
||||
});
|
||||
|
||||
@@ -23,7 +23,10 @@ describe('OnApplicationShutdown', () => {
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements OnApplicationShutdown {
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
public field: string;
|
||||
async onApplicationShutdown() {
|
||||
this.field = 'b-field';
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -34,8 +37,12 @@ describe('OnApplicationShutdown', () => {
|
||||
|
||||
@Injectable()
|
||||
class AA implements OnApplicationShutdown {
|
||||
public field: string;
|
||||
constructor(private bb: BB) {}
|
||||
onApplicationShutdown = Sinon.spy();
|
||||
|
||||
async onApplicationShutdown() {
|
||||
this.field = this.bb.field + '_a-field';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [B],
|
||||
@@ -51,8 +58,7 @@ describe('OnApplicationShutdown', () => {
|
||||
await app.init();
|
||||
await app.close();
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(aa.onApplicationShutdown, bb.onApplicationShutdown);
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -43,7 +43,10 @@ describe('OnModuleDestroy', () => {
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class BB implements OnModuleDestroy {
|
||||
onModuleDestroy = Sinon.spy();
|
||||
public field: string;
|
||||
async onModuleDestroy() {
|
||||
this.field = 'b-field';
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
@@ -54,10 +57,13 @@ describe('OnModuleDestroy', () => {
|
||||
|
||||
@Injectable()
|
||||
class AA implements OnModuleDestroy {
|
||||
public field: string;
|
||||
constructor(private bb: BB) {}
|
||||
onModuleDestroy = Sinon.spy();
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
this.field = this.bb.field + '_a-field';
|
||||
}
|
||||
}
|
||||
@Module({
|
||||
imports: [B],
|
||||
providers: [AA],
|
||||
@@ -72,8 +78,7 @@ describe('OnModuleDestroy', () => {
|
||||
await app.init();
|
||||
await app.close();
|
||||
|
||||
const aa = module.get(AA);
|
||||
const bb = module.get(BB);
|
||||
Sinon.assert.callOrder(aa.onModuleDestroy, bb.onModuleDestroy);
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -39,39 +39,11 @@ describe('OnModuleInit', () => {
|
||||
});
|
||||
|
||||
it('should sort modules by distance (topological sort) - DESC order', async () => {
|
||||
@Injectable()
|
||||
class CC implements OnModuleInit {
|
||||
public field: string;
|
||||
|
||||
async onModuleInit() {
|
||||
this.field = 'c-field';
|
||||
}
|
||||
}
|
||||
|
||||
@Module({})
|
||||
class C {
|
||||
static forRoot() {
|
||||
return {
|
||||
module: C,
|
||||
global: true,
|
||||
providers: [
|
||||
{
|
||||
provide: CC,
|
||||
useValue: new CC(),
|
||||
},
|
||||
],
|
||||
exports: [CC],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class BB implements OnModuleInit {
|
||||
public field: string;
|
||||
constructor(private cc: CC) {}
|
||||
|
||||
async onModuleInit() {
|
||||
this.field = this.cc.field + '_b-field';
|
||||
this.field = 'b-field';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,19 +68,14 @@ describe('OnModuleInit', () => {
|
||||
})
|
||||
class A {}
|
||||
|
||||
@Module({
|
||||
imports: [A, C.forRoot()],
|
||||
})
|
||||
class AppModule {}
|
||||
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
imports: [A],
|
||||
}).compile();
|
||||
|
||||
const app = module.createNestApplication();
|
||||
await app.init();
|
||||
|
||||
const instance = module.get(AA);
|
||||
expect(instance.field).to.equal('c-field_b-field_a-field');
|
||||
expect(instance.field).to.equal('b-field_a-field');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
const SIGNAL = process.argv[2];
|
||||
const SIGNAL_TO_LISTEN = process.argv[3];
|
||||
const USE_GRACEFUL_EXIT = process.argv[4] === 'graceful';
|
||||
|
||||
@Injectable()
|
||||
class TestInjectable
|
||||
@@ -30,16 +29,14 @@ class AppModule {}
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule, { logger: false });
|
||||
|
||||
const shutdownOptions = USE_GRACEFUL_EXIT ? { useProcessExit: true } : {};
|
||||
|
||||
if (SIGNAL_TO_LISTEN && SIGNAL_TO_LISTEN !== 'NONE') {
|
||||
app.enableShutdownHooks([SIGNAL_TO_LISTEN], shutdownOptions);
|
||||
app.enableShutdownHooks([SIGNAL_TO_LISTEN]);
|
||||
} else if (SIGNAL_TO_LISTEN !== 'NONE') {
|
||||
app.enableShutdownHooks([], shutdownOptions);
|
||||
app.enableShutdownHooks();
|
||||
}
|
||||
|
||||
await app.listen(1800);
|
||||
process.kill(process.pid, SIGNAL);
|
||||
}
|
||||
|
||||
void bootstrap();
|
||||
bootstrap();
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -1,63 +0,0 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { Controller, Injectable, Module } from '@nestjs/common';
|
||||
|
||||
class B {}
|
||||
|
||||
@Injectable()
|
||||
class A {
|
||||
constructor(b: B) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
class BImpl {
|
||||
constructor(a: A) {}
|
||||
}
|
||||
|
||||
@Controller()
|
||||
class AppController {
|
||||
constructor(a: A) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [AppController],
|
||||
providers: [A, { provide: B, useClass: BImpl }],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
describe('Circular custom providers', () => {
|
||||
it('should throw an exception (useClass + regular provider)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "A". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it('should throw an exception (2 factories)', async () => {
|
||||
try {
|
||||
const builder = Test.createTestingModule({
|
||||
providers: [
|
||||
{ provide: 'ABC', useFactory: () => ({}), inject: ['DEF'] },
|
||||
{ provide: 'DEF', useFactory: () => ({}), inject: ['ABC'] },
|
||||
],
|
||||
});
|
||||
await builder.compile();
|
||||
|
||||
expect(true).to.be.eql(false);
|
||||
} catch (err) {
|
||||
expect(err.message).to.be.eql(
|
||||
'A circular dependency has been detected inside "ABC". Please, make sure that each side of a bidirectional relationships are decorated with "forwardRef()". Note that circular relationships between custom providers (e.g., factories) are not supported since functions cannot be called more than once.',
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1,157 +0,0 @@
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as sinon from 'sinon';
|
||||
import { Global, Inject, Injectable, Module, Scope } from '@nestjs/common';
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule1 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule2 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule3 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule4 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule5 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule6 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule7 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule8 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule9 {}
|
||||
|
||||
@Global()
|
||||
@Module({})
|
||||
export class GlobalModule10 {}
|
||||
|
||||
@Injectable()
|
||||
class TransientProvider {}
|
||||
|
||||
@Injectable()
|
||||
class RequestProvider {}
|
||||
|
||||
@Injectable()
|
||||
class ForeignTransientProvider {}
|
||||
|
||||
@Injectable()
|
||||
export class Dependant {
|
||||
constructor(
|
||||
private readonly transientProvider: TransientProvider,
|
||||
|
||||
private readonly foreignTransientProvider: ForeignTransientProvider,
|
||||
|
||||
@Inject(RequestProvider)
|
||||
private readonly requestProvider: RequestProvider,
|
||||
) {}
|
||||
|
||||
public checkDependencies() {
|
||||
expect(this.transientProvider).to.be.instanceOf(TransientProvider);
|
||||
expect(this.foreignTransientProvider).to.be.instanceOf(
|
||||
ForeignTransientProvider,
|
||||
);
|
||||
expect(this.requestProvider).to.be.instanceOf(RequestProvider);
|
||||
}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: ForeignTransientProvider,
|
||||
scope: Scope.TRANSIENT,
|
||||
useClass: ForeignTransientProvider,
|
||||
},
|
||||
],
|
||||
exports: [ForeignTransientProvider],
|
||||
})
|
||||
export class ModuleWithForeignTransientProvider {}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: TransientProvider,
|
||||
scope: Scope.TRANSIENT,
|
||||
useClass: TransientProvider,
|
||||
},
|
||||
{
|
||||
provide: Dependant,
|
||||
scope: Scope.DEFAULT,
|
||||
useClass: Dependant,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class GlobalModuleWithTransientProviderAndDependant {}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: RequestProvider,
|
||||
scope: Scope.REQUEST,
|
||||
useFactory: () => {
|
||||
return new RequestProvider();
|
||||
},
|
||||
},
|
||||
],
|
||||
exports: [RequestProvider],
|
||||
})
|
||||
export class GlobalModuleWithRequestProvider {}
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
/*
|
||||
* ForeginTransientProvider will be resolved quickly because its host module is imported first.
|
||||
* IMPORTANT: Do not move this module, otherwise we may not catch future regressions.
|
||||
*/
|
||||
ModuleWithForeignTransientProvider,
|
||||
|
||||
GlobalModule1,
|
||||
GlobalModule2,
|
||||
GlobalModule3,
|
||||
GlobalModule4,
|
||||
GlobalModule5,
|
||||
GlobalModule6,
|
||||
GlobalModule7,
|
||||
GlobalModule8,
|
||||
GlobalModule9,
|
||||
GlobalModule10,
|
||||
GlobalModuleWithTransientProviderAndDependant,
|
||||
GlobalModuleWithRequestProvider,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
describe('Many global modules', () => {
|
||||
it('should inject request-scoped and transient-scoped providers from different modules', async () => {
|
||||
const moduleBuilder = Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
});
|
||||
const moduleRef = await moduleBuilder.compile();
|
||||
|
||||
const dependant = await moduleRef.resolve(Dependant);
|
||||
const checkDependenciesSpy = sinon.spy(dependant, 'checkDependencies');
|
||||
dependant.checkDependencies();
|
||||
|
||||
expect(checkDependenciesSpy.called).to.be.true;
|
||||
});
|
||||
});
|
||||
@@ -24,7 +24,7 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
});
|
||||
});
|
||||
describe('resolve()', () => {
|
||||
it('should return an array of request-scoped providers', async () => {
|
||||
it('should return an array of providers', async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [MultipleProvidersModule],
|
||||
});
|
||||
@@ -43,25 +43,5 @@ describe('Multiple providers under the same token ("each" feature)', () => {
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
});
|
||||
|
||||
it('should return an array of default-scoped providers', async () => {
|
||||
const builder = Test.createTestingModule({
|
||||
imports: [MultipleProvidersModule],
|
||||
});
|
||||
const testingModule = await builder.compile();
|
||||
|
||||
const multiProviderInstances = await testingModule.resolve<string>(
|
||||
'MULTI_PROVIDER',
|
||||
undefined,
|
||||
{
|
||||
each: true,
|
||||
},
|
||||
);
|
||||
|
||||
// @ts-expect-error: make sure "multiProviderInstances" is string[] not string
|
||||
multiProviderInstances.charAt;
|
||||
|
||||
expect(multiProviderInstances).to.be.eql(['A', 'B', 'C']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -118,17 +118,16 @@ describe('Optional factory provider deps', () => {
|
||||
} catch (err) {
|
||||
expect(err).to.be.instanceOf(UnknownDependenciesException);
|
||||
expect(err.message).to
|
||||
.equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument "MISSING_DEP" at index [0] is available in the RootTestModule context.
|
||||
.equal(`Nest can't resolve dependencies of the POSSIBLY_MISSING_DEP (?). Please make sure that the argument MISSING_DEP at index [0] is available in the RootTestModule context.
|
||||
|
||||
Potential solutions:
|
||||
- Is RootTestModule a valid NestJS module?
|
||||
- If "MISSING_DEP" is a provider, is it part of the current RootTestModule?
|
||||
- If "MISSING_DEP" is exported from a separate @Module, is that module imported within RootTestModule?
|
||||
- If MISSING_DEP is a provider, is it part of the current RootTestModule?
|
||||
- If MISSING_DEP is exported from a separate @Module, is that module imported within RootTestModule?
|
||||
@Module({
|
||||
imports: [ /* the Module containing "MISSING_DEP" */ ]
|
||||
imports: [ /* the Module containing MISSING_DEP */ ]
|
||||
})
|
||||
|
||||
For more common dependency resolution issues, see: https://docs.nestjs.com/faq/common-errors`);
|
||||
`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
import { Injectable, Scope, Module } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ContextIdFactory, REQUEST } from '@nestjs/core';
|
||||
import { expect } from 'chai';
|
||||
|
||||
describe('Request Scope Bubbling', () => {
|
||||
// 1. Define the "Poison" (Request Scoped Service)
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
class ChildService {
|
||||
// A random ID to verify uniqueness
|
||||
public readonly id = Math.random();
|
||||
}
|
||||
|
||||
// 2. Define the "Victim" (Singleton that depends on Request Scoped)
|
||||
@Injectable()
|
||||
class ParentService {
|
||||
constructor(public readonly child: ChildService) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [ChildService, ParentService],
|
||||
})
|
||||
class TestModule {}
|
||||
|
||||
it('should downgrade a Singleton to Request-Scoped if it depends on a Request-Scoped provider', async () => {
|
||||
// 3. Bootstrap the Module (using Test.createTestingModule instead of NestFactory)
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
imports: [TestModule],
|
||||
}).compile();
|
||||
|
||||
// 4. Simulate Request 1
|
||||
const contextId1 = ContextIdFactory.create();
|
||||
const parent1 = await moduleRef.resolve(ParentService, contextId1);
|
||||
|
||||
// 5. Simulate Request 2
|
||||
const contextId2 = ContextIdFactory.create();
|
||||
const parent2 = await moduleRef.resolve(ParentService, contextId2);
|
||||
|
||||
// 6. Assertions (The "Moment of Truth")
|
||||
|
||||
// The Child IDs should be different (Proof of Request Scope)
|
||||
expect(parent1.child.id).to.not.equal(parent2.child.id);
|
||||
|
||||
// The Parent instances should ALSO be different (Proof of Bubbling)
|
||||
// If Parent was a true Singleton, these would be equal.
|
||||
expect(parent1).to.not.equal(parent2);
|
||||
});
|
||||
});
|
||||
@@ -6,7 +6,7 @@ export class CircularModule {
|
||||
const a = {
|
||||
module: CircularModule,
|
||||
providers: [InputService],
|
||||
b: null as any,
|
||||
b: null,
|
||||
};
|
||||
a.b = a;
|
||||
return a;
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "ExternalContextCreator",
|
||||
"initTime": 0
|
||||
"token": "ExternalContextCreator"
|
||||
}
|
||||
},
|
||||
"208171089": {
|
||||
@@ -800,10 +800,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "ModulesContainer",
|
||||
"initTime": 0
|
||||
"token": "ModulesContainer"
|
||||
}
|
||||
},
|
||||
"-326832201": {
|
||||
@@ -816,10 +816,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "HttpAdapterHost",
|
||||
"initTime": 0
|
||||
"token": "HttpAdapterHost"
|
||||
}
|
||||
},
|
||||
"-702581189": {
|
||||
@@ -848,10 +848,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "SerializedGraph",
|
||||
"initTime": 0
|
||||
"token": "SerializedGraph"
|
||||
}
|
||||
},
|
||||
"-1251270035": {
|
||||
@@ -2197,22 +2197,6 @@
|
||||
},
|
||||
"id": "1976848738"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
@@ -2229,6 +2213,22 @@
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-1657371464": {
|
||||
"source": "-1673986099",
|
||||
"target": "1919157847",
|
||||
@@ -2263,22 +2263,6 @@
|
||||
},
|
||||
"id": "-1303681274"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-886102564": {
|
||||
"source": "208171089",
|
||||
"target": "671882984",
|
||||
@@ -2296,22 +2280,6 @@
|
||||
},
|
||||
"id": "-886102564"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-2003045613": {
|
||||
"source": "-377928898",
|
||||
"target": "-616397055",
|
||||
@@ -2344,6 +2312,38 @@
|
||||
},
|
||||
"id": "-881420795"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-1816180282": {
|
||||
"source": "-848516688",
|
||||
"target": "-1673986099",
|
||||
|
||||
@@ -60,10 +60,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "ExternalContextCreator",
|
||||
"initTime": 0
|
||||
"token": "ExternalContextCreator"
|
||||
}
|
||||
},
|
||||
"208171089": {
|
||||
@@ -784,10 +784,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "ModulesContainer",
|
||||
"initTime": 0
|
||||
"token": "ModulesContainer"
|
||||
}
|
||||
},
|
||||
"-326832201": {
|
||||
@@ -800,10 +800,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "HttpAdapterHost",
|
||||
"initTime": 0
|
||||
"token": "HttpAdapterHost"
|
||||
}
|
||||
},
|
||||
"-702581189": {
|
||||
@@ -832,10 +832,10 @@
|
||||
"sourceModuleName": "InternalCoreModule",
|
||||
"durable": false,
|
||||
"static": true,
|
||||
"scope": 0,
|
||||
"transient": false,
|
||||
"exported": true,
|
||||
"token": "SerializedGraph",
|
||||
"initTime": 0
|
||||
"token": "SerializedGraph"
|
||||
}
|
||||
},
|
||||
"-1251270035": {
|
||||
@@ -2181,22 +2181,6 @@
|
||||
},
|
||||
"id": "1976848738"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-2105726668": {
|
||||
"source": "-1803759743",
|
||||
"target": "1010833816",
|
||||
@@ -2213,6 +2197,22 @@
|
||||
},
|
||||
"id": "-2105726668"
|
||||
},
|
||||
"-21463590": {
|
||||
"source": "-1378706112",
|
||||
"target": "1004276345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "HelloModule",
|
||||
"sourceClassName": "HelloModule",
|
||||
"targetClassName": "Meta",
|
||||
"sourceClassToken": "HelloModule",
|
||||
"targetClassToken": "META",
|
||||
"targetModuleName": "HelloModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-21463590"
|
||||
},
|
||||
"-1657371464": {
|
||||
"source": "-1673986099",
|
||||
"target": "1919157847",
|
||||
@@ -2247,22 +2247,6 @@
|
||||
},
|
||||
"id": "-1303681274"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-886102564": {
|
||||
"source": "208171089",
|
||||
"target": "671882984",
|
||||
@@ -2280,22 +2264,6 @@
|
||||
},
|
||||
"id": "-886102564"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-2003045613": {
|
||||
"source": "-377928898",
|
||||
"target": "-616397055",
|
||||
@@ -2328,6 +2296,38 @@
|
||||
},
|
||||
"id": "-881420795"
|
||||
},
|
||||
"-831049991": {
|
||||
"source": "594986539",
|
||||
"target": "-1721730431",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "InputModule",
|
||||
"sourceClassName": "InputService",
|
||||
"targetClassName": "CircularService",
|
||||
"sourceClassToken": "InputService",
|
||||
"targetClassToken": "CircularService",
|
||||
"targetModuleName": "CircularModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-831049991"
|
||||
},
|
||||
"-2146943494": {
|
||||
"source": "-234035039",
|
||||
"target": "928565345",
|
||||
"metadata": {
|
||||
"type": "class-to-class",
|
||||
"sourceModuleName": "RequestChainModule",
|
||||
"sourceClassName": "RequestChainService",
|
||||
"targetClassName": "HelperService",
|
||||
"sourceClassToken": "RequestChainService",
|
||||
"targetClassToken": "HelperService",
|
||||
"targetModuleName": "HelperModule",
|
||||
"keyOrIndex": 0,
|
||||
"injectionType": "constructor"
|
||||
},
|
||||
"id": "-2146943494"
|
||||
},
|
||||
"-1816180282": {
|
||||
"source": "-848516688",
|
||||
"target": "-1673986099",
|
||||
|
||||
@@ -12,10 +12,9 @@ export interface Response<T> {
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class TransformInterceptor<T> implements NestInterceptor<
|
||||
T,
|
||||
Response<T>
|
||||
> {
|
||||
export class TransformInterceptor<T>
|
||||
implements NestInterceptor<T, Response<T>>
|
||||
{
|
||||
intercept(
|
||||
context: ExecutionContext,
|
||||
next: CallHandler<T>,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Controller } from '@nestjs/common';
|
||||
import { MessagePattern, Payload } from '@nestjs/microservices';
|
||||
import { ExternalSvcService } from './external-svc.service';
|
||||
import { CreateExternalSvcDto } from './dto/create-external-svc.dto';
|
||||
import { UpdateExternalSvcDto } from './dto/update-external-svc.dto';
|
||||
import { ExternalSvcService } from './external-svc.service';
|
||||
|
||||
@Controller()
|
||||
export class ExternalSvcController {
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"allowJs": true,
|
||||
"strictNullChecks": true,
|
||||
"outDir": "./dist",
|
||||
"paths": {
|
||||
"@nestjs/common": ["../../packages/common"],
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import { AppModule } from '../src/app.module';
|
||||
import chaiAsPromised = require('chai-as-promised');
|
||||
chai.use(chaiAsPromised);
|
||||
|
||||
describe('Lazy imports', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
});
|
||||
|
||||
it(`should allow imports of global modules`, async () => {
|
||||
await expect(app.init()).to.eventually.be.fulfilled;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
@@ -1,30 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
import { LazyController } from '../src/lazy.controller';
|
||||
|
||||
describe('Lazy Requested Scoped providers', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [LazyController],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should not recreate dependencies for default scope', async () => {
|
||||
const resultOne = await request(app.getHttpServer()).get('/lazy/request');
|
||||
|
||||
expect(resultOne.text).to.be.equal('Hi! Counter is 1');
|
||||
expect(resultOne.statusCode).to.be.equal(200);
|
||||
|
||||
const resultTwo = await request(app.getHttpServer()).get('/lazy/request');
|
||||
|
||||
expect(resultTwo.text).to.be.equal('Hi! Counter is 2');
|
||||
expect(resultTwo.statusCode).to.be.equal(200);
|
||||
});
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { TransientLazyModule } from '../src/transient.module';
|
||||
import { LazyController } from '../src/lazy.controller';
|
||||
import * as chai from 'chai';
|
||||
import { expect } from 'chai';
|
||||
import * as request from 'supertest';
|
||||
|
||||
describe('Lazy Transient providers', () => {
|
||||
let app: INestApplication;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
controllers: [LazyController],
|
||||
}).compile();
|
||||
|
||||
app = module.createNestApplication();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it('should not recreate dependencies for default scope', async () => {
|
||||
const resultOne = await request(app.getHttpServer()).get('/lazy/transient');
|
||||
|
||||
const resultTwo = await request(app.getHttpServer()).get('/lazy/transient');
|
||||
|
||||
expect(resultOne.text).to.be.equal('Hi! Counter is 1');
|
||||
expect(resultOne.statusCode).to.be.equal(200);
|
||||
|
||||
expect(resultTwo.text).to.be.equal('Hi! Counter is 2');
|
||||
expect(resultTwo.statusCode).to.be.equal(200);
|
||||
});
|
||||
});
|
||||
@@ -1,16 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { LazyModuleLoader } from '@nestjs/core';
|
||||
import { EagerModule } from './eager.module';
|
||||
import { GlobalModule } from './global.module';
|
||||
import { LazyModule } from './lazy.module';
|
||||
|
||||
@Module({
|
||||
imports: [GlobalModule, EagerModule],
|
||||
})
|
||||
export class AppModule {
|
||||
constructor(public loader: LazyModuleLoader) {}
|
||||
|
||||
async onApplicationBootstrap() {
|
||||
await this.loader.load(() => LazyModule);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
import { Module, Injectable } from '@nestjs/common';
|
||||
import { GlobalService } from './global.module';
|
||||
|
||||
@Injectable()
|
||||
export class EagerService {
|
||||
private counter = 0;
|
||||
constructor(public globalService: GlobalService) {}
|
||||
|
||||
sayHello() {
|
||||
this.counter++;
|
||||
return 'Hi! Counter is ' + this.counter;
|
||||
}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [EagerService],
|
||||
})
|
||||
export class EagerModule {}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { Module, Injectable, Global } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class GlobalService {
|
||||
constructor() {}
|
||||
}
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [GlobalService],
|
||||
exports: [GlobalService],
|
||||
})
|
||||
export class GlobalModule {}
|
||||
@@ -1,28 +0,0 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { LazyModuleLoader } from '@nestjs/core';
|
||||
|
||||
@Controller('lazy')
|
||||
export class LazyController {
|
||||
constructor(private lazyLoadModule: LazyModuleLoader) {}
|
||||
|
||||
@Get('transient')
|
||||
async exec() {
|
||||
const { TransientLazyModule } = await import('./transient.module');
|
||||
const moduleRef = await this.lazyLoadModule.load(() => TransientLazyModule);
|
||||
|
||||
const { TransientService } = await import('./transient.service');
|
||||
const _service = await moduleRef.resolve(TransientService);
|
||||
|
||||
return _service.eager();
|
||||
}
|
||||
@Get('request')
|
||||
async execRequestScope() {
|
||||
const { RequestLazyModule } = await import('./request.module');
|
||||
const moduleRef = await this.lazyLoadModule.load(() => RequestLazyModule);
|
||||
|
||||
const { RequestService } = await import('./request.service');
|
||||
const _service = await moduleRef.resolve(RequestService);
|
||||
|
||||
return _service.eager();
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import { Module, Injectable } from '@nestjs/common';
|
||||
import { GlobalService } from './global.module';
|
||||
|
||||
@Injectable()
|
||||
export class LazyService {
|
||||
constructor(public globalService: GlobalService) {}
|
||||
}
|
||||
|
||||
@Module({
|
||||
providers: [LazyService],
|
||||
})
|
||||
export class LazyModule {}
|
||||
@@ -1,8 +0,0 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
await app.listen(3000);
|
||||
}
|
||||
void bootstrap();
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { EagerService } from './eager.module';
|
||||
import { GlobalService } from './global.module';
|
||||
import { RequestService } from './request.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [RequestService, GlobalService, EagerService],
|
||||
exports: [RequestService],
|
||||
})
|
||||
export class RequestLazyModule {}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { EagerService } from './eager.module';
|
||||
|
||||
@Injectable({ scope: Scope.REQUEST })
|
||||
export class RequestService {
|
||||
constructor(private eagerService: EagerService) {}
|
||||
|
||||
eager() {
|
||||
return this.eagerService.sayHello();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { GlobalService } from './global.module';
|
||||
import { EagerService } from './eager.module';
|
||||
import { TransientService } from './transient.service';
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
providers: [TransientService, GlobalService, EagerService],
|
||||
exports: [TransientService],
|
||||
})
|
||||
export class TransientLazyModule {}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Injectable, Scope } from '@nestjs/common';
|
||||
import { EagerService } from './eager.module';
|
||||
|
||||
@Injectable({ scope: Scope.TRANSIENT })
|
||||
export class TransientService {
|
||||
constructor(private eagerService: EagerService) {}
|
||||
|
||||
eager() {
|
||||
return this.eagerService.sayHello();
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user