├── .dockerignore
├── .env
├── .eslintrc.js
├── .gitignore
├── .prettierrc
├── Dockerfile
├── README.md
├── docker-compose.yml
├── nest-cli.json
├── nodemon.json
├── ormconfig.ts
├── package-lock.json
├── package.json
├── src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
├── auth
│ ├── auth.controller.ts
│ ├── auth.dto.ts
│ ├── auth.module.ts
│ ├── auth.service.ts
│ └── jwt.strategy.ts
├── config
│ ├── config.default.ts
│ ├── config.interface.ts
│ ├── config.module.ts
│ └── config.service.ts
├── database
│ ├── database.module.ts
│ ├── db.error.ts
│ └── db.interface.ts
├── entities
│ ├── Category.ts
│ ├── Tag.ts
│ ├── comment.ts
│ ├── post.ts
│ └── user.ts
├── guards
│ └── seller.guard.ts
├── main.ts
├── post
│ ├── post.controller.ts
│ ├── post.dto.ts
│ ├── post.module.ts
│ └── post.service.ts
├── shared
│ ├── http-exception.filter.ts
│ ├── logging.interceptor.ts
│ ├── shared.module.ts
│ └── user.service.ts
├── swagger
│ ├── swagger.config.ts
│ ├── swagger.interface.ts
│ └── swagger.ts
└── types
│ ├── order.ts
│ ├── payload.ts
│ ├── product.ts
│ └── user.ts
├── test
├── app.e2e-spec.ts
└── jest-e2e.json
├── tsconfig.build.json
└── tsconfig.json
/.dockerignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | npm-debug.log
3 | Dockerfile*
4 | docker-compose*
5 | .dockerignore
6 | .git
7 | .gitignore
8 | README.md
9 | LICENSE
10 | .vscode
11 |
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | ENV=development
2 | PORT=3000
3 | SECRET_KEY=asdafewfsca
4 | DB_TYPE=mysql
5 | MYSQL_USER=root
6 | MYSQL_PASSWORD=root
7 | MYSQL_DB=test
8 | MYSQL_DB_HOST=mysql
9 | MYSQL_DB_PORT=3306
10 | DB_DIALECT=mysql
11 | DB_CHARSET=utf8mb4
12 | DB_COLLATE=utf8mb4_unicode_ci
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | parserOptions: {
4 | project: 'tsconfig.json',
5 | sourceType: 'module',
6 | },
7 | plugins: ['@typescript-eslint/eslint-plugin'],
8 | extends: [
9 | 'plugin:@typescript-eslint/eslint-recommended',
10 | 'plugin:@typescript-eslint/recommended',
11 | 'prettier',
12 | 'prettier/@typescript-eslint',
13 | ],
14 | root: true,
15 | env: {
16 | node: true,
17 | jest: true,
18 | },
19 | rules: {
20 | '@typescript-eslint/interface-name-prefix': 'off',
21 | '@typescript-eslint/explicit-function-return-type': 'off',
22 | '@typescript-eslint/no-explicit-any': 'off',
23 | },
24 | };
25 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # compiled output
2 | /dist
3 | /node_modules
4 |
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 |
13 | # OS
14 | .DS_Store
15 |
16 | # Tests
17 | /coverage
18 | /.nyc_output
19 |
20 | # IDEs and editors
21 | /.idea
22 | .project
23 | .classpath
24 | .c9/
25 | *.launch
26 | .settings/
27 | *.sublime-workspace
28 |
29 | # IDE - VSCode
30 | .vscode/*
31 | !.vscode/settings.json
32 | !.vscode/tasks.json
33 | !.vscode/launch.json
34 | !.vscode/extensions.json
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all"
4 | }
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM node:carbon
2 | # Create app directory
3 | WORKDIR /usr/src/app
4 | # Bundle app source
5 | COPY . .
6 | # npm install
7 | RUN npm install
8 | # Run npm install --global grpc --unsafe-perm
9 | EXPOSE 3000 9204
10 | CMD [ "npm", "run", "debug" ]
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Nest JS Tutorials
2 | https://www.youtube.com/watch?v=8d75-sTi4UI&list=PLIGDNOJWiL1_AhUGgmwz7RhyXwX5aVLj4
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master
11 | [travis-url]: https://travis-ci.org/nestjs/nest
12 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux
13 | [linux-url]: https://travis-ci.org/nestjs/nest
14 |
15 | A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
31 |
32 | ## Description
33 |
34 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
35 |
36 | ## Installation
37 |
38 | ```bash
39 | $ npm install
40 | ```
41 |
42 | ## Running the app
43 |
44 | ```bash
45 | # development
46 | $ npm run start
47 |
48 | # watch mode
49 | $ npm run start:dev
50 |
51 | # production mode
52 | $ npm run start:prod
53 | ```
54 |
55 | ## Test
56 |
57 | ```bash
58 | # unit tests
59 | $ npm run test
60 |
61 | # e2e tests
62 | $ npm run test:e2e
63 |
64 | # test coverage
65 | $ npm run test:cov
66 | ```
67 |
68 | ## Support
69 |
70 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
71 |
72 | ## Stay in touch
73 |
74 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
75 | - Website - [https://nestjs.com](https://nestjs.com/)
76 | - Twitter - [@nestframework](https://twitter.com/nestframework)
77 |
78 | ## License
79 |
80 | Nest is [MIT licensed](LICENSE).
81 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3"
2 | services:
3 | node:
4 | build: .
5 | ports:
6 | - 3000:3000
7 | volumes:
8 | - ./:/usr/src/app
9 | depends_on:
10 | - mysql
11 | mysql:
12 | image: mysql:5.7
13 | environment:
14 | MYSQL_ROOT_PASSWORD: root
15 | MYSQL_DATABASE: test
16 | MYSQL_USER: root
17 | MYSQL_PASSWORD: root
18 | volumes:
19 | - mysql-data:/var/lib/mysql
20 | ports:
21 | - 3306:3306
22 | volumes:
23 | mysql-data: {}
--------------------------------------------------------------------------------
/nest-cli.json:
--------------------------------------------------------------------------------
1 | {
2 | "collection": "@nestjs/schematics",
3 | "sourceRoot": "src"
4 | }
5 |
--------------------------------------------------------------------------------
/nodemon.json:
--------------------------------------------------------------------------------
1 | {
2 | "watch": ["src"],
3 | "ext": "ts",
4 | "ignore": ["src/**/*.spec.ts"],
5 | "exec": "ts-node -r tsconfig-paths/register src/main.ts"
6 | }
7 |
--------------------------------------------------------------------------------
/ormconfig.ts:
--------------------------------------------------------------------------------
1 | import * as dotenv from 'dotenv';
2 | dotenv.config();
3 |
4 | module.exports = {
5 | type: 'mysql',
6 | host: process.env.MYSQL_DB_HOST,
7 | port: process.env.MYSQL_DB_PORT,
8 | username: process.env.MYSQL_USER,
9 | password: process.env.MYSQL_PASSWORD,
10 | database: process.env.MYSQL_DB,
11 | synchronize: true,
12 | logging: true,
13 | entities: [
14 | 'src/entities/*.ts',
15 | ],
16 | migrations: [
17 | 'src/migration/**/*.ts',
18 | ],
19 | subscribers: [
20 | 'src/subscriber/**/*.ts',
21 | ],
22 | cli: {
23 | entitiesDir: 'src/app/models',
24 | migrationsDir: 'src/migration',
25 | subscribersDir: 'src/subscriber',
26 | },
27 | };
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nestjs-project",
3 | "version": "0.0.1",
4 | "description": "",
5 | "author": "",
6 | "private": true,
7 | "license": "UNLICENSED",
8 | "scripts": {
9 | "prebuild": "rimraf dist",
10 | "build": "nest build",
11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
12 | "debug": "nodemon",
13 | "start:dev": "nest start --watch",
14 | "start:debug": "nest start --debug --watch",
15 | "start:prod": "node dist/main",
16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
17 | "test": "jest",
18 | "test:watch": "jest --watch",
19 | "test:cov": "jest --coverage",
20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
21 | "test:e2e": "jest --config ./test/jest-e2e.json",
22 | "typeorm:cli": "ts-node ./node_modules/typeorm/cli -f ./ormconfig.ts",
23 | "db:drop": "npm run typeorm:cli schema:drop",
24 | "migration:generate": "typeorm migration:create -n",
25 | "db:create": "ts-node src/script/create-db.ts",
26 | "db:migrate": "npm run typeorm:cli migration:run",
27 | "db:revert": "npm run typeorm:cli migration:revert",
28 | "db:sync": "npm run typeorm:cli schema:sync && npm run db:migrate"
29 | },
30 | "dependencies": {
31 | "@nestjs/common": "^7.0.0",
32 | "@nestjs/core": "^7.0.0",
33 | "@nestjs/mongoose": "^7.0.1",
34 | "@nestjs/passport": "^7.0.0",
35 | "@nestjs/platform-express": "^7.0.0",
36 | "@nestjs/swagger": "^4.5.8",
37 | "@nestjs/typeorm": "^7.1.0",
38 | "@types/jsonwebtoken": "^8.5.0",
39 | "@types/passport-jwt": "^3.0.3",
40 | "bcrypt": "^4.0.1",
41 | "bcryptjs": "^2.4.3",
42 | "class-transformer": "^0.2.3",
43 | "class-validator": "^0.12.2",
44 | "dotenv": "^8.2.0",
45 | "jsonwebtoken": "^8.5.1",
46 | "mongoose": "^5.9.16",
47 | "mysql": "^2.18.1",
48 | "mysql2": "^2.1.0",
49 | "nodemon": "^2.0.4",
50 | "passport": "^0.4.1",
51 | "passport-jwt": "^4.0.0",
52 | "reflect-metadata": "^0.1.13",
53 | "rimraf": "^3.0.2",
54 | "rxjs": "^6.5.4",
55 | "swagger-ui-express": "^4.1.4",
56 | "typeorm": "^0.2.25"
57 | },
58 | "devDependencies": {
59 | "@nestjs/cli": "^7.0.0",
60 | "@nestjs/schematics": "^7.0.0",
61 | "@nestjs/testing": "^7.0.0",
62 | "@types/express": "^4.17.3",
63 | "@types/jest": "25.1.4",
64 | "@types/node": "^13.9.1",
65 | "@types/supertest": "^2.0.8",
66 | "@typescript-eslint/eslint-plugin": "^2.23.0",
67 | "@typescript-eslint/parser": "^2.23.0",
68 | "eslint": "^6.8.0",
69 | "eslint-config-prettier": "^6.10.0",
70 | "eslint-plugin-import": "^2.20.1",
71 | "jest": "^25.1.0",
72 | "prettier": "^1.19.1",
73 | "supertest": "^4.0.2",
74 | "ts-jest": "25.2.1",
75 | "ts-loader": "^6.2.1",
76 | "ts-node": "^8.6.2",
77 | "tsconfig-paths": "^3.9.0",
78 | "typescript": "^3.7.4"
79 | },
80 | "jest": {
81 | "moduleFileExtensions": [
82 | "js",
83 | "json",
84 | "ts"
85 | ],
86 | "rootDir": "src",
87 | "testRegex": ".spec.ts$",
88 | "transform": {
89 | "^.+\\.(t|j)s$": "ts-jest"
90 | },
91 | "coverageDirectory": "../coverage",
92 | "testEnvironment": "node"
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/app.controller.spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { AppController } from './app.controller';
3 | import { AppService } from './app.service';
4 |
5 | describe('AppController', () => {
6 | let appController: AppController;
7 |
8 | beforeEach(async () => {
9 | const app: TestingModule = await Test.createTestingModule({
10 | controllers: [AppController],
11 | providers: [AppService],
12 | }).compile();
13 |
14 | appController = app.get(AppController);
15 | });
16 |
17 | describe('root', () => {
18 | it('should return "Hello World!"', () => {
19 | expect(appController.getHello()).toBe('Hello World!');
20 | });
21 | });
22 | });
23 |
--------------------------------------------------------------------------------
/src/app.controller.ts:
--------------------------------------------------------------------------------
1 | import { Controller, Get, UseFilters, UseGuards, Body, Post } from '@nestjs/common';
2 | import { AppService } from './app.service';
3 | import { ApiProperty } from '@nestjs/swagger';
4 | import { AuthGuard } from '@nestjs/passport';
5 | @Controller()
6 |
7 | export class AppController {
8 | constructor(private readonly appService: AppService) {}
9 |
10 | @Get()
11 | getHello(): string {
12 | return this.appService.getHello();
13 | }
14 |
15 | @Get('/ping')
16 | @UseGuards(AuthGuard('jwt'))
17 | getHelloPing(): string {
18 | return this.appService.getHello();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/app.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
2 | import { AppController } from './app.controller';
3 | import { DatabaseModule } from './database/database.module';
4 | import UserEntity from './entities/User';
5 | import PostEntity from './entities/Post';
6 | import CommnentEntity from './entities/Comment';
7 |
8 | import TagEntity from './entities/Tag';
9 | import CategoryEntity from './entities/Category';
10 |
11 | import { AppService } from './app.service';
12 | import { SharedModule } from './shared/shared.module';
13 | import { AuthModule } from './auth/auth.module';
14 |
15 | @Module({
16 | imports: [
17 | DatabaseModule.forRoot({ entities: [
18 | PostEntity,
19 | UserEntity,
20 | CommnentEntity,
21 | CategoryEntity,
22 | TagEntity
23 | ]}),
24 | AuthModule,
25 | SharedModule
26 | ],
27 | controllers: [AppController],
28 | providers: [AppService],
29 | })
30 | export class AppModule {
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/app.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | @Injectable()
4 | export class AppService {
5 | getHello(): string {
6 | return 'Hello World!';
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/auth/auth.controller.ts:
--------------------------------------------------------------------------------
1 | import { Body, Controller, Post, Get } from '@nestjs/common';
2 |
3 | import { UserService } from '../shared/user.service';
4 | import { Payload } from '../types/payload';
5 | import { LoginDTO, RegisterDTO } from './auth.dto';
6 | import { AuthService } from './auth.service';
7 |
8 | @Controller('auth')
9 | export class AuthController {
10 | constructor(
11 | private userService: UserService,
12 | private authService: AuthService,
13 | ) {}
14 |
15 | @Post('login')
16 | async login(@Body() userDTO: LoginDTO) {
17 | const user = await this.userService.findByLogin(userDTO);
18 | const payload: Payload = {
19 | username: user.username
20 | };
21 | const token = await this.authService.signPayload(payload);
22 | return { token };
23 | }
24 |
25 | @Post('register')
26 | async register(@Body() userDTO: RegisterDTO) {
27 | const user = await this.userService.create(userDTO);
28 | return { 'message': 'user creatd successfully' };
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/auth/auth.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from '@nestjs/swagger';
2 | import { MinLength, IsNotEmpty, IsEmail } from 'class-validator';
3 |
4 | export class LoginDTO {
5 |
6 | @ApiProperty()
7 | @MinLength(6)
8 | @IsNotEmpty()
9 | username: string;
10 |
11 | @ApiProperty()
12 | @MinLength(8)
13 | @IsNotEmpty()
14 | password: string;
15 | }
16 |
17 | export class RegisterDTO {
18 |
19 | @ApiProperty()
20 | @MinLength(6)
21 | @IsNotEmpty()
22 | username: string;
23 |
24 | @ApiProperty()
25 | @MinLength(8)
26 | @IsNotEmpty()
27 | password: string;
28 |
29 | }
30 |
--------------------------------------------------------------------------------
/src/auth/auth.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 |
3 | import { SharedModule } from '../shared/shared.module';
4 | import { AuthController } from './auth.controller';
5 | import { AuthService } from './auth.service';
6 | import { JwtStrategy } from './jwt.strategy';
7 |
8 | @Module({
9 | imports: [SharedModule],
10 | controllers: [AuthController],
11 | providers: [AuthService, JwtStrategy],
12 | })
13 | export class AuthModule {}
14 |
--------------------------------------------------------------------------------
/src/auth/auth.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 | import { sign } from 'jsonwebtoken';
3 |
4 | import { UserService } from '../shared/user.service';
5 | import { Payload } from '../types/payload';
6 | import User from 'src/entities/User';
7 |
8 | @Injectable()
9 | export class AuthService {
10 | constructor(private userService: UserService) {}
11 |
12 | async signPayload(payload: Payload) {
13 | return sign(payload, process.env.SECRET_KEY, { expiresIn: '12h' });
14 | }
15 |
16 | async validateUser(payload: Payload): Promise {
17 | return await this.userService.findByUserName(payload.username)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/auth/jwt.strategy.ts:
--------------------------------------------------------------------------------
1 | import { PassportStrategy } from '@nestjs/passport'
2 | import { AuthService } from './auth.service';
3 | import { ExtractJwt, Strategy, VerifyCallback } from 'passport-jwt'
4 | import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
5 | @Injectable()
6 | export class JwtStrategy extends PassportStrategy(Strategy) {
7 | constructor(private readonly authservice: AuthService) {
8 | super({
9 | jwtFromRequest: ExtractJwt.fromHeader('access-token'),
10 | secretOrKey: process.env.SECRET_KEY
11 | });
12 | }
13 | async validate(payload: any, done: VerifyCallback) {
14 | try {
15 | const user = await this.authservice.validateUser(payload);
16 | if (!user) {
17 | return done(new HttpException('Unauthorized access', HttpStatus.UNAUTHORIZED), null)
18 | }
19 | return done(null, payload);
20 | } catch (err) {
21 | throw new HttpException('Unauthorized access', HttpStatus.UNAUTHORIZED)
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/config/config.default.ts:
--------------------------------------------------------------------------------
1 | import { ConfigData } from './config.interface';
2 |
3 | export const DEFAULT_CONFIG: ConfigData = {
4 | env: '',
5 | port: 3000,
6 | db: undefined,
7 | logLevel: 'info',
8 | };
9 |
--------------------------------------------------------------------------------
/src/config/config.interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Configuration for the database connection.
3 | */
4 | export interface ConfigDBData {
5 | type: string;
6 | user: string;
7 | pass: string;
8 | name: string;
9 | host: string;
10 | port: number;
11 | dialect: string;
12 | charset: string;
13 | collate: string;
14 | }
15 |
16 | /**
17 | * Configuration data for the app.
18 | */
19 | export interface ConfigData {
20 | env: string;
21 |
22 | /** The port number of the http server to listen on. */
23 | port: number;
24 |
25 | /** Database connection details. */
26 | db?: ConfigDBData;
27 |
28 | /**
29 | * The log level to use.
30 | * @example 'verbose', 'info', 'warn', 'error'
31 | */
32 | logLevel: string;
33 | }
34 |
--------------------------------------------------------------------------------
/src/config/config.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { ConfigService } from './config.service';
3 | import * as dotenv from 'dotenv';
4 | const configFactory = {
5 | provide: ConfigService,
6 | useFactory: () => {
7 | dotenv.config();
8 | const config = new ConfigService();
9 | config.lofusingDotEnv();
10 | return config;
11 | },
12 | };
13 |
14 | @Module({
15 | imports: [],
16 | controllers: [],
17 | providers: [configFactory],
18 | exports: [configFactory],
19 | })
20 | export class ConfigModule {}
21 |
--------------------------------------------------------------------------------
/src/config/config.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@nestjs/common';
2 |
3 | import { DEFAULT_CONFIG } from './config.default';
4 | import { ConfigData, ConfigDBData } from './config.interface';
5 |
6 | /**
7 | * Provides a means to access the application configuration.
8 | */
9 | @Injectable()
10 | export class ConfigService {
11 | private config: ConfigData;
12 |
13 | constructor(data: ConfigData = DEFAULT_CONFIG) {
14 | this.config = data;
15 | }
16 |
17 | /**
18 | * Loads the config from environment variables.
19 | */
20 | public lofusingDotEnv() {
21 | this.config = this.parseConfigFromEnv(process.env);
22 | console.log(this.config);
23 | }
24 |
25 | private parseConfigFromEnv(env: NodeJS.ProcessEnv): ConfigData {
26 | return {
27 | env: env.NODE_ENV || DEFAULT_CONFIG.env,
28 | port: env.PORT ? parseInt(env.PORT, 10) : DEFAULT_CONFIG.port,
29 | db: this.parseDbConfigFromEnv(env) || DEFAULT_CONFIG.db,
30 | logLevel: env.LOG_LEVEL || DEFAULT_CONFIG.logLevel,
31 | };
32 | }
33 |
34 | private parseDbConfigFromEnv(env: NodeJS.ProcessEnv): ConfigDBData {
35 | return {
36 | type: env.DB_TYPE || '',
37 | user: env.MYSQL_USER || '',
38 | pass: env.MYSQL_PASSWORD || '',
39 | name: env.MYSQL_DB || '',
40 | host: env.MYSQL_DB_HOST || '',
41 | port: parseInt(env.MYSQL_DB_PORT || 'NaN', 10),
42 | dialect: env.DB_DIALECT || '',
43 | charset: env.DB_CHARSET || '',
44 | collate: env.DB_COLLATE || '',
45 | };
46 | }
47 | public get(): Readonly {
48 | return this.config;
49 | }
50 | }
51 |
52 | /*
53 | @Injectable()
54 | export class ConfigService {
55 | private readonly logger = new Logger(ConfigService.name);
56 |
57 | private envPath: any;
58 | private readonly nodeEnv: string = process.env.NODE_ENV
59 | ? process.env.NODE_ENV
60 | : 'development';
61 |
62 | private readonly envConfig: { [key: string]: string };
63 | constructor() {
64 | this.logger.log('SERVICE INIT');
65 | switch (this.nodeEnv) {
66 | case 'test':
67 | this.envPath = path.resolve(__dirname, '../../.env');
68 | break;
69 | case 'production':
70 | this.envPath = path.resolve(__dirname, '../../.env');
71 | break;
72 | case 'development':
73 | this.envPath = path.resolve(__dirname, '../../.env');
74 | break;
75 | default:
76 | throw new Error('Specify the NODE_ENV variable');
77 | }
78 | this.envConfig = dotenv.parse(fs.readFileSync(this.envPath));
79 | }
80 |
81 | get(key: string): string {
82 | return this.envConfig[key];
83 | }
84 | }
85 | */
86 |
--------------------------------------------------------------------------------
/src/database/database.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, DynamicModule, NotImplementedException } from '@nestjs/common';
2 | import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
3 | import { ConfigDBData } from '../config/config.interface';
4 | import { ConfigModule } from '../config/config.module';
5 | import { ConfigService } from '../config/config.service';
6 | import { DbConfig } from './db.interface';
7 | import { DbConfigError, DbError } from './db.error';
8 |
9 | @Module({})
10 |
11 | export class DatabaseModule {
12 |
13 | public static getConnectionOptions(config: ConfigService, dbconfig: DbConfig): TypeOrmModuleOptions {
14 | const dbdata = config.get().db;
15 | console.log(config);
16 | let connectionOptions: TypeOrmModuleOptions;
17 |
18 | if (!dbdata) {
19 | throw new DbConfigError('Database config is missing');
20 | }
21 | switch (dbdata.type) {
22 | case 'mysql':
23 | connectionOptions = this.getConnectionOptionsMysql(dbdata);
24 | break;
25 | default:
26 | throw new NotImplementedException(`Database type '${dbdata.type}' not supported`);
27 | }
28 | return {
29 | ...connectionOptions,
30 | entities: dbconfig.entities,
31 | logging: true,
32 | };
33 | }
34 | private static getConnectionOptionsSqlite(dbdata: any): TypeOrmModuleOptions {
35 | throw new NotImplementedException(`Database type '${dbdata.type}' not supported`);
36 | }
37 | private static getConnectionOptionsMysql(dbdata: ConfigDBData): TypeOrmModuleOptions {
38 | return {
39 | type: 'mysql',
40 | host: dbdata.host,
41 | port: dbdata.port,
42 | username: dbdata.user,
43 | password: dbdata.pass,
44 | database: dbdata.name,
45 | charset: dbdata.charset,
46 | extra: {
47 | collate: dbdata.collate,
48 | dialect: dbdata.dialect,
49 | },
50 | };
51 | }
52 | public static forRoot(dbconfig: DbConfig): DynamicModule {
53 | return {
54 | module: DatabaseModule,
55 | imports: [
56 | TypeOrmModule.forRootAsync({
57 | imports: [ConfigModule],
58 | useFactory: (configService: ConfigService) => DatabaseModule.getConnectionOptions(configService, dbconfig),
59 | inject: [ConfigService],
60 | }),
61 | ],
62 | controllers: [],
63 | providers: [],
64 | exports: [],
65 | };
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/database/db.error.ts:
--------------------------------------------------------------------------------
1 | export class DbError extends Error {
2 | public constructor(message = 'Unknown database error') {
3 | super(message);
4 | }
5 | }
6 |
7 | // tslint:disable-next-line: max-classes-per-file
8 | export class DbConfigError extends DbError {
9 | public constructor(message = 'Database configuration error') {
10 | super(message);
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/src/database/db.interface.ts:
--------------------------------------------------------------------------------
1 | import { ConnectionOptions } from 'typeorm';
2 |
3 | /**
4 | * Contains configuration options for the TypeORM database.
5 | * Note that connection details, such as host and credentials, come from the environment variables, via the main config.
6 | */
7 | export interface DbConfig {
8 | entities: ConnectionOptions['entities'];
9 | }
10 |
--------------------------------------------------------------------------------
/src/entities/Category.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty } from 'class-validator';
2 | import {
3 | Column,
4 | CreateDateColumn,
5 | Entity,
6 | ManyToOne,
7 | OneToMany,
8 | PrimaryGeneratedColumn,
9 | UpdateDateColumn,
10 | OneToOne,
11 | } from 'typeorm';
12 | import Comment from './Comment';
13 | import User from './User';
14 | import Post from './Post';
15 |
16 | @Entity()
17 | class Category {
18 | @PrimaryGeneratedColumn()
19 | public id: number;
20 |
21 | @Column()
22 | @IsNotEmpty()
23 | public category: string;
24 |
25 | @OneToOne(() => Post, post => post.category)
26 | public post: Post;
27 |
28 | @Column()
29 | @CreateDateColumn()
30 | public createdAt: Date;
31 |
32 | @Column()
33 | @UpdateDateColumn()
34 | public updatedAt: Date;
35 | }
36 |
37 | export default Category;
38 |
--------------------------------------------------------------------------------
/src/entities/Tag.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty } from 'class-validator';
2 | import {
3 | Column,
4 | CreateDateColumn,
5 | Entity,
6 | ManyToOne,
7 | PrimaryGeneratedColumn,
8 | UpdateDateColumn,
9 | } from 'typeorm';
10 | import Post from './Post';
11 |
12 | @Entity()
13 | class Tag {
14 | @PrimaryGeneratedColumn()
15 | public id: number;
16 |
17 | @Column()
18 | @IsNotEmpty()
19 | public text: string;
20 |
21 | @ManyToOne(() => Post, post => post.tags, { eager: true, onDelete: 'CASCADE' })
22 | public post: Post;
23 |
24 | @Column()
25 | @CreateDateColumn()
26 | public createdAt: Date;
27 |
28 | @Column()
29 | @UpdateDateColumn()
30 | public updatedAt: Date;
31 | }
32 |
33 |
34 | export default Tag;
--------------------------------------------------------------------------------
/src/entities/comment.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty } from 'class-validator';
2 | import {
3 | Column,
4 | CreateDateColumn,
5 | Entity,
6 | ManyToOne,
7 | PrimaryGeneratedColumn,
8 | UpdateDateColumn,
9 | } from 'typeorm';
10 | import Post from './Post';
11 | import User from './User';
12 |
13 | @Entity()
14 | class Comment {
15 | @PrimaryGeneratedColumn()
16 | public id: number;
17 |
18 | @Column()
19 | @IsNotEmpty()
20 | public text: string;
21 |
22 | @ManyToOne(() => User, user => user.comments, { eager: true, onDelete: 'CASCADE' })
23 | public user: User;
24 |
25 | @ManyToOne(() => Post, post => post.comments, { eager: true, onDelete: 'CASCADE' })
26 | public post: Post;
27 |
28 | @Column()
29 | @CreateDateColumn()
30 | public createdAt: Date;
31 |
32 | @Column()
33 | @UpdateDateColumn()
34 | public updatedAt: Date;
35 | }
36 |
37 | export default Comment;
38 |
--------------------------------------------------------------------------------
/src/entities/post.ts:
--------------------------------------------------------------------------------
1 | import { IsNotEmpty } from 'class-validator';
2 | import {
3 | Column,
4 | CreateDateColumn,
5 | Entity,
6 | ManyToOne,
7 | OneToMany,
8 | PrimaryGeneratedColumn,
9 | UpdateDateColumn,
10 | OneToOne,
11 | } from 'typeorm';
12 | import Comment from './Comment';
13 | import User from './User';
14 | import Tag from './Tag';
15 | import Category from './Category';
16 |
17 | @Entity()
18 | class Post {
19 | @PrimaryGeneratedColumn()
20 | public id: number;
21 |
22 | @Column()
23 | @IsNotEmpty()
24 | public title: string;
25 |
26 | @ManyToOne(() => User, user => user.posts, {eager : true, onDelete: 'CASCADE'})
27 | public user: User;
28 |
29 | @OneToMany(() => Comment, comment => comment.post)
30 | public comments: Comment[];
31 |
32 | @OneToMany(() => Tag, tag => tag.post)
33 | public tags: Tag[];
34 |
35 | @OneToOne(() => Category, category => category.post)
36 | public category: Category;
37 |
38 | @Column()
39 | public url: string;
40 |
41 | @Column()
42 | public text: string;
43 |
44 | @Column()
45 | @CreateDateColumn()
46 | public createdAt: Date;
47 |
48 | @Column()
49 | @UpdateDateColumn()
50 | public updatedAt: Date;
51 | }
52 |
53 | export default Post;
54 |
--------------------------------------------------------------------------------
/src/entities/user.ts:
--------------------------------------------------------------------------------
1 | import * as bcrypt from 'bcryptjs';
2 | import { IsNotEmpty, Length } from 'class-validator';
3 | import {
4 | Column,
5 | CreateDateColumn,
6 | Entity,
7 | OneToMany,
8 | PrimaryGeneratedColumn,
9 | Unique,
10 | UpdateDateColumn,
11 | } from 'typeorm';
12 | import Comment from './Comment';
13 | import Post from './Post';
14 |
15 | @Entity()
16 | @Unique(['username'])
17 | export class User {
18 | @PrimaryGeneratedColumn()
19 | public id: number;
20 |
21 | @Column()
22 | @Length(4, 20)
23 | @IsNotEmpty()
24 | public username: string;
25 |
26 | @Column()
27 | @Length(4, 100)
28 | @IsNotEmpty()
29 | public password: string;
30 |
31 | @OneToMany(() => Post, post => post.user)
32 | public posts: Post[]
33 |
34 | @OneToMany(() => Comment, comment => comment.user)
35 | public comments: Comment[]
36 |
37 | @Column()
38 | @CreateDateColumn()
39 | public createdAt: Date;
40 |
41 | @Column()
42 | @UpdateDateColumn()
43 | public updatedAt: Date;
44 |
45 | public hashPassword() {
46 | this.password = bcrypt.hashSync(this.password, 8);
47 | }
48 |
49 | public checkIfUnencryptedPasswordIsValid(unencryptedPassword: string) {
50 | return bcrypt.compareSync(unencryptedPassword, this.password);
51 | }
52 | }
53 |
54 | export default User;
55 |
--------------------------------------------------------------------------------
/src/guards/seller.guard.ts:
--------------------------------------------------------------------------------
1 | import { CanActivate, HttpStatus } from "@nestjs/common";
2 | import {ExecutionContext,HttpException} from '@nestjs/common'
3 |
4 | export class SellerGuard implements CanActivate {
5 | canActivate(context: ExecutionContext ): boolean | Promise | import("rxjs").Observable {
6 | const request = context.switchToHttp().getRequest();
7 | const user = request.user;
8 | if(user.seller){
9 | return true;
10 | }
11 | throw new HttpException('Unauthorized Exception', HttpStatus.UNAUTHORIZED);
12 | }
13 | }
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import { NestFactory } from '@nestjs/core';
2 | import { AppModule } from './app.module';
3 | import { SwaggerModule } from '@nestjs/swagger';
4 | import { createdocument } from './swagger/swagger';
5 | import { LoggingInterceptor } from './shared/logging.interceptor';
6 |
7 | async function bootstrap() {
8 | const app = await NestFactory.create(AppModule);
9 | SwaggerModule.setup('api', app, createdocument(app));
10 | app.useGlobalInterceptors(new LoggingInterceptor());
11 | await app.listen(3000);
12 | }
13 | bootstrap();
14 |
--------------------------------------------------------------------------------
/src/post/post.controller.ts:
--------------------------------------------------------------------------------
1 | import { Body, Controller, Post, Get, BadRequestException, Param } from '@nestjs/common';
2 |
3 | import { PostService } from './post.service';
4 | import { PostDTO } from './post.dto';
5 |
6 | @Controller('post')
7 | export class AuthController {
8 | constructor(
9 | private postService: PostService,
10 | ) {}
11 |
12 | @Get('/:id')
13 | async getPost(@Param('id') id: string) {
14 | try {
15 | return await this.postService.getPostByid(id);
16 | } catch(err){
17 | throw new BadRequestException(err);
18 | }
19 | }
20 | @Post('/')
21 | async login(@Body() postDTO: PostDTO) {
22 | try {
23 | return await this.postService.create(postDTO);
24 | } catch(err){
25 | throw new BadRequestException(err);
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/post/post.dto.ts:
--------------------------------------------------------------------------------
1 | import { ApiProperty } from "@nestjs/swagger";
2 | import { IsString, IsNotEmpty } from "class-validator";
3 |
4 | export class PostDTO {
5 | @ApiProperty()
6 | @IsString()
7 | @IsNotEmpty()
8 | title: string;
9 |
10 | @ApiProperty()
11 | @IsString()
12 | @IsNotEmpty()
13 | description: string;
14 | }
--------------------------------------------------------------------------------
/src/post/post.module.ts:
--------------------------------------------------------------------------------
1 | import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
2 | import { TypeOrmModule } from '@nestjs/typeorm';
3 | import Post from '../entities/Post';
4 |
5 | @Module({
6 | imports: [
7 | TypeOrmModule.forFeature([Post])
8 | ],
9 | controllers: [],
10 | providers: [],
11 | })
12 | export class AppModule {
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/post/post.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpException, HttpStatus, Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common';
2 | import { InjectModel } from '@nestjs/mongoose';
3 | import * as bcrypt from 'bcrypt';
4 | import { InjectRepository } from '@nestjs/typeorm';
5 |
6 | import { LoginDTO, RegisterDTO } from '../auth/auth.dto';
7 | import { Payload } from '../types/payload';
8 | import { Repository } from 'typeorm';
9 | import User from '../entities/User';
10 | import Post from 'src/entities/Post';
11 | import { PostDTO } from './post.dto';
12 |
13 | @Injectable()
14 | export class PostService {
15 | constructor(@InjectRepository(Post) private postRepo: Repository) {}
16 |
17 | // created new post
18 | async create(postDTO: PostDTO): Promise {
19 | try {
20 | const { title, description } = postDTO;
21 | const newPost = new Post();
22 | newPost.title = title;
23 | newPost.text = description;
24 | return await this.postRepo.save(newPost);
25 | }catch(err){
26 | throw new BadRequestException(err);
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/shared/http-exception.filter.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ArgumentsHost,
3 | Catch,
4 | ExceptionFilter,
5 | HttpException,
6 | HttpStatus,
7 | } from '@nestjs/common';
8 |
9 | @Catch()
10 | export class HttpExceptionFilter implements ExceptionFilter {
11 | catch(exception: HttpException, host: ArgumentsHost) {
12 | const ctx = host.switchToHttp();
13 | const response = ctx.getResponse();
14 | const request = ctx.getRequest();
15 | const status = exception.getStatus
16 | ? exception.getStatus()
17 | : HttpStatus.INTERNAL_SERVER_ERROR;
18 |
19 | const errorResponse = {
20 | code: status,
21 | timestamp: new Date().toLocaleDateString(),
22 | path: request.url,
23 | method: request.method,
24 | message:
25 | status !== HttpStatus.INTERNAL_SERVER_ERROR
26 | ? exception.message || exception.message || null
27 | : 'Internal server error',
28 | };
29 |
30 | response.status(status).json(errorResponse);
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/shared/logging.interceptor.ts:
--------------------------------------------------------------------------------
1 | import {
2 | ExecutionContext,
3 | Injectable,
4 | Logger,
5 | NestInterceptor,
6 | CallHandler,
7 | } from '@nestjs/common';
8 | import { Observable } from 'rxjs';
9 | import { tap } from 'rxjs/operators';
10 |
11 | @Injectable()
12 | export class LoggingInterceptor implements NestInterceptor {
13 | intercept(context: ExecutionContext, next: CallHandler): Observable {
14 | const now = Date.now();
15 | const req = context.switchToHttp().getRequest();
16 |
17 | const method = req.method;
18 | const url = req.url;
19 |
20 | return next
21 | .handle()
22 | .pipe(
23 | tap(() =>
24 | Logger.log(
25 | `${method} ${url} ${Date.now() - now}ms`,
26 | context.getClass().name,
27 | ),
28 | ),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/shared/shared.module.ts:
--------------------------------------------------------------------------------
1 | import { Module } from '@nestjs/common';
2 | import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
3 | import { MongooseModule } from '@nestjs/mongoose';
4 |
5 | import { HttpExceptionFilter } from './http-exception.filter';
6 | import { LoggingInterceptor } from './logging.interceptor';
7 | import { UserService } from './user.service';
8 | import { TypeOrmModule } from '@nestjs/typeorm';
9 | import User from 'src/entities/User';
10 |
11 | @Module({
12 | imports: [TypeOrmModule.forFeature([User])],
13 | providers: [
14 | UserService,
15 | {
16 | provide: APP_FILTER,
17 | useClass: HttpExceptionFilter,
18 | },
19 | {
20 | provide: APP_INTERCEPTOR,
21 | useClass: LoggingInterceptor,
22 | }
23 | ],
24 | exports: [UserService],
25 | })
26 | export class SharedModule {}
27 |
--------------------------------------------------------------------------------
/src/shared/user.service.ts:
--------------------------------------------------------------------------------
1 | import { HttpException, HttpStatus, Injectable, BadRequestException, UnauthorizedException } from '@nestjs/common';
2 | import { InjectModel } from '@nestjs/mongoose';
3 | import * as bcrypt from 'bcrypt';
4 | import { InjectRepository } from '@nestjs/typeorm';
5 |
6 | import { LoginDTO, RegisterDTO } from '../auth/auth.dto';
7 | import { Payload } from '../types/payload';
8 | import { Repository } from 'typeorm';
9 | import User from '../entities/User';
10 | import { validate } from 'class-validator';
11 |
12 | @Injectable()
13 | export class UserService {
14 | constructor(@InjectRepository(User) private userRepo: Repository) {}
15 |
16 | // user register
17 | async create(userDTO: RegisterDTO): Promise {
18 | try {
19 | const { username, password } = userDTO;
20 | const user = await this.userRepo.findOne({ username });
21 | if (user) {
22 | throw new HttpException('User already exists', HttpStatus.BAD_REQUEST);
23 | }
24 | const newUser = new User();
25 | newUser.username = username;
26 | newUser.password = password;
27 | /*const errors = await validate(newUser);
28 | if(errors && errors.length > 0){
29 | throw new BadRequestException(errors);
30 | } */
31 | newUser.hashPassword();
32 | return await this.userRepo.save(newUser);
33 | }catch(err){
34 | throw new BadRequestException(err);
35 | }
36 | }
37 |
38 | async findByUserName(username: string): Promise {
39 | try {
40 | const user = await this.userRepo.findOne({ username });
41 | if (user) {
42 | throw new HttpException('User does not exist in system', HttpStatus.UNAUTHORIZED);
43 | }
44 | return user;
45 | }catch(err){
46 | throw new UnauthorizedException(err);
47 | }
48 | }
49 |
50 | async findByLogin(userDTO: LoginDTO) {
51 | const { username, password } = userDTO;
52 | const user = await this.userRepo.findOne({ username });
53 | if (!user) {
54 | throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
55 | }
56 | if (user.checkIfUnencryptedPasswordIsValid(password)) {
57 | return this.sanitizeUser(user);
58 | } else {
59 | throw new HttpException('Invalid credentials', HttpStatus.UNAUTHORIZED);
60 | }
61 | }
62 |
63 | sanitizeUser(user: User) {
64 | const obj = {... user};
65 | delete obj['password'];
66 | return obj;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/swagger/swagger.config.ts:
--------------------------------------------------------------------------------
1 | import { SwaggerConfig } from './swagger.interface';
2 |
3 | /**
4 | * Configuration for the swagger UI (found at /api).
5 | * Change this to suit your app!
6 | */
7 | export const SWAGGER_CONFIG: SwaggerConfig = {
8 | title: 'Nest js Template',
9 | description: 'Template',
10 | version: '1.0',
11 | tags: ['Template'],
12 | };
13 |
--------------------------------------------------------------------------------
/src/swagger/swagger.interface.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Specifies configuration for the swagger UI (found at /api).
3 | */
4 | export interface SwaggerConfig {
5 | title: string;
6 | description: string;
7 | version: string;
8 | tags: string[];
9 | }
10 |
--------------------------------------------------------------------------------
/src/swagger/swagger.ts:
--------------------------------------------------------------------------------
1 | import { INestApplication } from "@nestjs/common";
2 | import {DocumentBuilder, OpenAPIObject, SwaggerModule} from '@nestjs/swagger'
3 | import { SWAGGER_CONFIG } from "./swagger.config";
4 |
5 |
6 | export function createdocument(app: INestApplication): OpenAPIObject {
7 | const builder = new DocumentBuilder()
8 | .setTitle(SWAGGER_CONFIG.title)
9 | .setDescription(SWAGGER_CONFIG.description)
10 | .addBearerAuth({ type: 'http', scheme: 'bearer', bearerFormat: 'JWT' }, 'access-token')
11 | .setVersion(SWAGGER_CONFIG.version);
12 | for (const tag of SWAGGER_CONFIG.tags) {
13 | builder.addTag(tag);
14 | }
15 | const options = builder.build();
16 |
17 | return SwaggerModule.createDocument(app, options);
18 | }
--------------------------------------------------------------------------------
/src/types/order.ts:
--------------------------------------------------------------------------------
1 | import { Document } from 'mongoose';
2 |
3 | import { User } from './user';
4 | import { Product } from './product';
5 |
6 | interface ProductOrder {
7 | product: Product;
8 | quantity: number;
9 | }
10 |
11 | export interface Order extends Document {
12 | owner: User;
13 | totalPrice: Number;
14 | products: ProductOrder[];
15 | created: Date;
16 | }
17 |
--------------------------------------------------------------------------------
/src/types/payload.ts:
--------------------------------------------------------------------------------
1 | export interface Payload {
2 | username: string;
3 | iat?: number;
4 | expiresIn?: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/types/product.ts:
--------------------------------------------------------------------------------
1 | import { Document } from 'mongoose';
2 |
3 | import { User } from './user';
4 |
5 | export interface Product extends Document {
6 | owner: User;
7 | title: string;
8 | image: string;
9 | description: string;
10 | price: number;
11 | created: Date;
12 | }
13 |
--------------------------------------------------------------------------------
/src/types/user.ts:
--------------------------------------------------------------------------------
1 | import { Document } from 'mongoose';
2 |
3 | export interface Address {
4 | addr1: string;
5 | addr2: string;
6 | city: string;
7 | state: string;
8 | country: string;
9 | zip: number;
10 | }
11 |
12 | export interface User extends Document {
13 | username: string;
14 | readonly password: string;
15 | seller: boolean;
16 | address: Address;
17 | created: Date;
18 | }
19 |
--------------------------------------------------------------------------------
/test/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { Test, TestingModule } from '@nestjs/testing';
2 | import { INestApplication } from '@nestjs/common';
3 | import * as request from 'supertest';
4 | import { AppModule } from './../src/app.module';
5 |
6 | describe('AppController (e2e)', () => {
7 | let app: INestApplication;
8 |
9 | beforeEach(async () => {
10 | const moduleFixture: TestingModule = await Test.createTestingModule({
11 | imports: [AppModule],
12 | }).compile();
13 |
14 | app = moduleFixture.createNestApplication();
15 | await app.init();
16 | });
17 |
18 | it('/ (GET)', () => {
19 | return request(app.getHttpServer())
20 | .get('/')
21 | .expect(200)
22 | .expect('Hello World!');
23 | });
24 | });
25 |
--------------------------------------------------------------------------------
/test/jest-e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "moduleFileExtensions": ["js", "json", "ts"],
3 | "rootDir": ".",
4 | "testEnvironment": "node",
5 | "testRegex": ".e2e-spec.ts$",
6 | "transform": {
7 | "^.+\\.(t|j)s$": "ts-jest"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"]
4 | }
5 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "declaration": true,
5 | "removeComments": true,
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "target": "es2017",
9 | "sourceMap": true,
10 | "outDir": "./dist",
11 | "baseUrl": "./",
12 | "incremental": true
13 | },
14 | "exclude": ["node_modules", "dist"]
15 | }
16 |
--------------------------------------------------------------------------------