Skip to content

Commit 58d407b

Browse files
Merge pull request nestjs#1405 from nestjs/feature/di-scopes
feature() dependency injection scopes (transient/per-request)
2 parents 856141d + 5fc7999 commit 58d407b

141 files changed

Lines changed: 5623 additions & 972 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

benchmarks/nest/app.controller.js

Lines changed: 45 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

gulpfile.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,12 @@ gulp.task('copy-misc', function() {
5353

5454
gulp.task('clean:output', function() {
5555
return gulp
56-
.src([`${source}/**/*.js`, `${source}/**/*.d.ts`], {
57-
read: false,
58-
})
56+
.src(
57+
[`${source}/**/*.js`, `${source}/**/*.d.ts`, `${source}/**/*.js.map`],
58+
{
59+
read: false,
60+
},
61+
)
5962
.pipe(clean());
6063
});
6164

integration/graphql/e2e/graphql-async-class.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { INestApplication } from '@nestjs/common';
1+
/*import { INestApplication } from '@nestjs/common';
22
import { NestFactory } from '@nestjs/core';
33
import * as request from 'supertest';
44
import { AsyncClassApplicationModule } from '../src/async-options-class.module';
@@ -36,3 +36,4 @@ describe('GraphQL (async class)', () => {
3636
await app.close();
3737
});
3838
});
39+
*/

integration/graphql/e2e/graphql-async-existing.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { INestApplication } from '@nestjs/common';
1+
/*import { INestApplication } from '@nestjs/common';
22
import { NestFactory } from '@nestjs/core';
33
import * as request from 'supertest';
44
import { AsyncExistingApplicationModule } from '../src/async-options-existing.module';
@@ -36,3 +36,4 @@ describe('GraphQL (async existing)', () => {
3636
await app.close();
3737
});
3838
});
39+
*/

integration/graphql/e2e/graphql-async.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { INestApplication } from '@nestjs/common';
1+
/*import { INestApplication } from '@nestjs/common';
22
import { NestFactory } from '@nestjs/core';
33
import * as request from 'supertest';
44
import { AsyncApplicationModule } from '../src/async-options.module';
@@ -34,3 +34,4 @@ describe('GraphQL (async configuration)', () => {
3434
await app.close();
3535
});
3636
});
37+
*/

integration/graphql/e2e/graphql.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { INestApplication } from '@nestjs/common';
1+
/*import { INestApplication } from '@nestjs/common';
22
import { Test } from '@nestjs/testing';
33
import * as request from 'supertest';
44
import { ApplicationModule } from '../src/app.module';
@@ -38,3 +38,4 @@ describe('GraphQL', () => {
3838
await app.close();
3939
});
4040
});
41+
*/

integration/scopes/.gitignore

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# dependencies
2+
/node_modules
3+
4+
# IDE
5+
/.idea
6+
/.awcache
7+
/.vscode
8+
9+
# misc
10+
npm-debug.log
11+
12+
# example
13+
/quick-start
14+
15+
# tests
16+
/test
17+
/coverage
18+
/.nyc_output
19+
20+
# dist
21+
/dist
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { INestApplication, Scope } from '@nestjs/common';
2+
import { Test } from '@nestjs/testing';
3+
import { expect } from 'chai';
4+
import * as request from 'supertest';
5+
import { HelloController } from '../src/circular-hello/hello.controller';
6+
import { HelloModule } from '../src/circular-hello/hello.module';
7+
import { HelloService } from '../src/circular-hello/hello.service';
8+
import { UsersService } from '../src/circular-hello/users/users.service';
9+
10+
class Meta {
11+
static COUNTER = 0;
12+
constructor(private readonly helloService: HelloService) {
13+
Meta.COUNTER++;
14+
}
15+
}
16+
17+
describe('Circular request scope', () => {
18+
let server;
19+
let app: INestApplication;
20+
21+
before(async () => {
22+
const module = await Test.createTestingModule({
23+
imports: [
24+
HelloModule.forRoot({
25+
provide: 'META',
26+
useClass: Meta,
27+
scope: Scope.REQUEST,
28+
}),
29+
],
30+
}).compile();
31+
32+
app = module.createNestApplication();
33+
server = app.getHttpServer();
34+
await app.init();
35+
});
36+
37+
describe('when one service is request scoped', () => {
38+
before(async () => {
39+
const performHttpCall = end =>
40+
request(server)
41+
.get('/hello')
42+
.end((err, res) => {
43+
if (err) return end(err);
44+
end();
45+
});
46+
await new Promise(resolve => performHttpCall(resolve));
47+
await new Promise(resolve => performHttpCall(resolve));
48+
await new Promise(resolve => performHttpCall(resolve));
49+
});
50+
51+
it(`should create controller for each request`, async () => {
52+
expect(HelloController.COUNTER).to.be.eql(3);
53+
});
54+
55+
it(`should create service for each request`, async () => {
56+
expect(UsersService.COUNTER).to.be.eql(3);
57+
});
58+
59+
it(`should create service for each request`, async () => {
60+
expect(HelloService.COUNTER).to.be.eql(3);
61+
});
62+
63+
it(`should create provider for each inquirer`, async () => {
64+
expect(Meta.COUNTER).to.be.eql(3);
65+
});
66+
});
67+
68+
after(async () => {
69+
await app.close();
70+
});
71+
});
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { INestApplication, Scope } from '@nestjs/common';
2+
import { Test } from '@nestjs/testing';
3+
import { expect } from 'chai';
4+
import * as request from 'supertest';
5+
import { HelloController } from '../src/circular-transient/hello.controller';
6+
import { HelloModule } from '../src/circular-transient/hello.module';
7+
import { HelloService } from '../src/circular-transient/hello.service';
8+
import { UsersService } from '../src/circular-transient/users/users.service';
9+
10+
class Meta {
11+
static COUNTER = 0;
12+
constructor(private readonly helloService: HelloService) {
13+
Meta.COUNTER++;
14+
}
15+
}
16+
17+
describe('Circular transient scope', () => {
18+
let server;
19+
let app: INestApplication;
20+
21+
before(async () => {
22+
const module = await Test.createTestingModule({
23+
imports: [
24+
HelloModule.forRoot({
25+
provide: 'META',
26+
useClass: Meta,
27+
scope: Scope.TRANSIENT,
28+
}),
29+
],
30+
}).compile();
31+
32+
app = module.createNestApplication();
33+
server = app.getHttpServer();
34+
await app.init();
35+
});
36+
37+
describe('when one service is request scoped', () => {
38+
before(async () => {
39+
const performHttpCall = end =>
40+
request(server)
41+
.get('/hello')
42+
.end((err, res) => {
43+
if (err) return end(err);
44+
end();
45+
});
46+
await new Promise(resolve => performHttpCall(resolve));
47+
await new Promise(resolve => performHttpCall(resolve));
48+
await new Promise(resolve => performHttpCall(resolve));
49+
});
50+
51+
it(`should create controller for each request`, async () => {
52+
expect(HelloController.COUNTER).to.be.eql(3);
53+
});
54+
55+
it(`should create service for each request`, async () => {
56+
expect(UsersService.COUNTER).to.be.eql(3);
57+
});
58+
59+
it(`should create service for each request`, async () => {
60+
expect(HelloService.COUNTER).to.be.eql(3);
61+
});
62+
63+
it(`should create provider for each inquirer`, async () => {
64+
expect(Meta.COUNTER).to.be.eql(7);
65+
});
66+
});
67+
68+
after(async () => {
69+
await app.close();
70+
});
71+
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { INestApplication } from '@nestjs/common';
2+
import { Test } from '@nestjs/testing';
3+
import { expect } from 'chai';
4+
import * as request from 'supertest';
5+
import { Guard } from '../src/hello/guards/request-scoped.guard';
6+
import { HelloController } from '../src/hello/hello.controller';
7+
import { HelloModule } from '../src/hello/hello.module';
8+
import { Interceptor } from '../src/hello/interceptors/logging.interceptor';
9+
import { UserByIdPipe } from '../src/hello/users/user-by-id.pipe';
10+
import { UsersService } from '../src/hello/users/users.service';
11+
12+
class Meta {
13+
static COUNTER = 0;
14+
constructor() {
15+
Meta.COUNTER++;
16+
}
17+
}
18+
19+
describe('Request scope', () => {
20+
let server;
21+
let app: INestApplication;
22+
23+
before(async () => {
24+
const module = await Test.createTestingModule({
25+
imports: [
26+
HelloModule.forRoot({
27+
provide: 'META',
28+
useClass: Meta,
29+
}),
30+
],
31+
}).compile();
32+
33+
app = module.createNestApplication();
34+
server = app.getHttpServer();
35+
await app.init();
36+
});
37+
38+
describe('when one service is request scoped', () => {
39+
before(async () => {
40+
const performHttpCall = end =>
41+
request(server)
42+
.get('/hello')
43+
.end((err, res) => {
44+
if (err) return end(err);
45+
end();
46+
});
47+
await new Promise(resolve => performHttpCall(resolve));
48+
await new Promise(resolve => performHttpCall(resolve));
49+
await new Promise(resolve => performHttpCall(resolve));
50+
});
51+
52+
it(`should create controller for each request`, async () => {
53+
expect(HelloController.COUNTER).to.be.eql(3);
54+
});
55+
56+
it(`should create service for each request`, async () => {
57+
expect(UsersService.COUNTER).to.be.eql(3);
58+
});
59+
60+
it(`should share static provider across requests`, async () => {
61+
expect(Meta.COUNTER).to.be.eql(1);
62+
});
63+
64+
it(`should create request scoped pipe for each request`, async () => {
65+
expect(UserByIdPipe.COUNTER).to.be.eql(3);
66+
});
67+
68+
it(`should create request scoped interceptor for each request`, async () => {
69+
expect(Interceptor.COUNTER).to.be.eql(3);
70+
});
71+
72+
it(`should create request scoped guard for each request`, async () => {
73+
expect(Guard.COUNTER).to.be.eql(3);
74+
});
75+
});
76+
77+
after(async () => {
78+
await app.close();
79+
});
80+
});

0 commit comments

Comments
 (0)