Nest: [Documentation] TypeScript dependency management

Created on 11 Apr 2017  路  12Comments  路  Source: nestjs/nest

In the documentation you mentioned:

It is incredibly easy to manage dependencies with TypeScript. If you are not TypeScript enthusiast and you work with plain JavaScript, you have to do it in this way:

This is slightly confusing since there's not mention of how to "manage dependencies with TypeScript" and the examples in the documentation will not run unless I add the @Dependencies([UsersService]) decorator. Without this decorator I'd get this exception:

[Nest] 2031   - 4/11/2017, 1:08:53 PM   [ExceptionsHandler] Cannot read property 'getUser' of undefined
TypeError: Cannot read property 'getUser' of undefined

Most helpful comment

Hi @alexcorvi @ThomRick,

Let's add:

"emitDecoratorMetadata": true,

to your tsconfig.json file. It is my mistake - I didn't mention about it in the documentation.

EDIT: I answered using my old account.

All 12 comments

Hi @alexcorvi,

Can you paste your code here? With Typescript, Nest should deduce dependency just by type.

Hi all,

For my use case I had to use @Inject() parameter decorator instead of @Dependencies():

import {Controller, Inject, RequestMapping, RequestMethod} from 'nest.js';
import {UserService} from './user.service';

@Controller({ path: 'users'})
export class UserController {

  constructor(@Inject(UserService) private userService) {}

  @RequestMapping()
  public async getAllUsers(request, response) {
    const users = await this.userService.getAllUsers();
    response.status(200).json(users);
  }

  @RequestMapping({ path: '/:id' })
  public async getUser(request, response) {
    const user = await this.userService.getUser(request.params.id);
    response.status(200).json(user);
  }

  @RequestMapping({ method: RequestMethod.POST })
  public async addUser(request, response) {
    const message = await this.userService.addUser(request.body.user);
    response.status(201).json(message);
  }
}

I use the 1.0.0-rc8

Hi @ThomRick,
Let's try:

constructor(private userService: UserService) {}

With Typescript, it should works. If not - it is an issue.

Controller

import {Dependencies,RequestMethod,Controller,RequestMapping} from 'nest.js';
import {Request,Response,NextFunction} from "@types/express"
import {UsersService} from "./users.service";
import {UserRequest} from "./user.typing";

@Controller({path: 'users'})
export class UsersController {

    constructor(private usersService: UsersService) {
        console.log(usersService,this.usersService);
    }

    @RequestMapping()
    async getAllUsers(req:UserRequest, res:Response) {
        const users = await this.usersService.getAllUsers();
        res.status(200).json(users);
    }

    @RequestMapping({ path: '/:id' })
    async getUser(req:UserRequest, res:Response) {
        console.log(req.params.id);
        const user = await this.usersService.getUser(req.params.id);
        res.status(200).json(user);
    }

    @RequestMapping({ method: RequestMethod.POST })
    async addUser(req:UserRequest, res:Response) {
        const msg = await this.usersService.getUser(req.body.user);
        res.status(201).json(msg);
    }
}

Service

Component()
export class UsersService {

    private users = [
        { id: 1, name: "John Doe" },
        { id: 2, name: "Alex Corvi" },
        { id: 3, name: "Who Knows" },
    ];

    async getAllUsers():Promise<User[]> {
        return await this.users;
    }

    async getUser(id: string):Promise<User> {
        const user = this.users.find((user) => user.id.toString() === id);
        if (!user) throw new HttpException("User not found", 404);
        return await user;
    }

    async addUser(user:User):Promise<UserAdded> {
        this.users.push(user);
        return await {
            id:user.id,
            name:user.name,
            date:new Date().getTime()
        };
    }
}

Module

import {Module} from 'nest.js';
import {UsersController} from './users.controller';
import {UsersService} from './users.service';

@Module({
    controllers: [ UsersController ],
    components: [ UsersService ],
})
export class UsersModule {}

Result

Now when running ts-node src/server.ts Here's the output:

[Nest] 712   - 4/12/2017, 10:43:56 PM   [NestFactory] Starting Nest application...
[Nest] 712   - 4/12/2017, 10:43:56 PM   [InstanceLoader] ApplicationModule dependencies initialized
undefined undefined
[Nest] 712   - 4/12/2017, 10:43:56 PM   [InstanceLoader] UsersModule dependencies initialized
[Nest] 712   - 4/12/2017, 10:43:56 PM   [RoutesResolver] UsersController:
[Nest] 712   - 4/12/2017, 10:43:56 PM   [RouterBuilder] Mapped {/, GET} route
[Nest] 712   - 4/12/2017, 10:43:56 PM   [RouterBuilder] Mapped {/:id, GET} route
[Nest] 712   - 4/12/2017, 10:43:56 PM   [RouterBuilder] Mapped {/, POST} route
[Nest] 712   - 4/12/2017, 10:43:56 PM   [NestApplication] Nest application is ready!

Navigating to localhost:3000/users gives the following error in the console:

[Nest] 712   - 4/12/2017, 10:52:53 PM   [ExceptionsHandler] Cannot read property 'getAllUsers' of undefined
TypeError: Cannot read property 'getAllUsers' of undefined

@alexcorvi - please, show your tsconfig.json file.

tsconfig.json

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es6",
        "noImplicitAny": true,
        "sourceMap": false,
        "strictNullChecks": true,
        "experimentalDecorators": true,
        "outDir": "./dist/"
    },
    "exclude": ["node_modules"],
    "files": ["./src/server.ts"]
}

Hey @kamilmysliwiec,

Sorry it doesn't work without the @Inject() decorator :-/
I try to log what happened in the UserController constructor:

Without @Inject() decorator:

[Nest] 3931   - 2017-04-13 08:52:16   [NestFactory] Starting Nest application...
- - - - UserController constructor - - - -
undefined
- - - - UserController constructor - - - -
[Nest] 3931   - 2017-04-13 08:52:16   [InstanceLoader] ApplicationModule dependencies initialized
[Nest] 3931   - 2017-04-13 08:52:16   [RoutesResolver] UserController:
[Nest] 3931   - 2017-04-13 08:52:16   [RouterBuilder] Mapped {/, GET} route
[Nest] 3931   - 2017-04-13 08:52:16   [RouterBuilder] Mapped {/:id, GET} route
[Nest] 3931   - 2017-04-13 08:52:16   [RouterBuilder] Mapped {/, POST} route
[Nest] 3931   - 2017-04-13 08:52:16   [NestApplication] Nest application is ready!
Application is listening at port 8080

With the @Inject() decorator:

[Nest] 3937   - 2017-04-13 08:52:42   [NestFactory] Starting Nest application...
- - - - UserController constructor - - - -
UserService {
  users:
   [ { id: '1', name: 'John Doe' },
     { id: '2', name: 'Alice Cairo' },
     { id: '3', name: 'Who Knows' } ] }
- - - - UserController constructor - - - -
[Nest] 3937   - 2017-04-13 08:52:42   [InstanceLoader] ApplicationModule dependencies initialized
[Nest] 3937   - 2017-04-13 08:52:42   [RoutesResolver] UserController:
[Nest] 3937   - 2017-04-13 08:52:42   [RouterBuilder] Mapped {/, GET} route
[Nest] 3937   - 2017-04-13 08:52:42   [RouterBuilder] Mapped {/:id, GET} route
[Nest] 3937   - 2017-04-13 08:52:42   [RouterBuilder] Mapped {/, POST} route
[Nest] 3937   - 2017-04-13 08:52:42   [NestApplication] Nest application is ready!

Hey @smsites,

You find the right solution to the issue with the "emitDecoratorMetadata": true

Let's see my tsconfig.json :

{
  "compilerOptions": {
    "module": "commonjs",
    "target": "es6",
    "sourceMap": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "outDir": "dist",
    "typeRoots": [
      "node_modules/@types"
    ]
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "node_modules"
  ]
}

And the UserController class code:

import {Controller, RequestMapping, RequestMethod} from 'nest.js';
import {UserService} from './user.service';

@Controller({ path: 'users'})
export class UserController {

  constructor(private userService: UserService) {
    console.log('- - - - UserController constructor - - - -');
    console.log('UserService : ', this.userService);
    console.log('- - - - UserController constructor - - - -');
  }

  @RequestMapping()
  public getAllUsers(request, response) {
    this.userService.getAllUsers()
      .then(users => {
        response.status(200).json(users);
      });
  }

  @RequestMapping({ path: '/:id' })
  public getUser(request, response) {
    this.userService.getUser(request.params.id)
      .then(user => {
        response.status(200).json(user);
      });
  }

  @RequestMapping({ method: RequestMethod.POST })
  public addUser(request, response) {
    this.userService.addUser(request.body.user)
      .then(message => {
        response.status(201).json(message);
      });
  }
}

No need the @Inject() param decorator.

Hi @alexcorvi @ThomRick,

Let's add:

"emitDecoratorMetadata": true,

to your tsconfig.json file. It is my mistake - I didn't mention about it in the documentation.

EDIT: I answered using my old account.

@kamilmysliwiec Confirmed, this solved it for me.

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

hackboy picture hackboy  路  3Comments

tronginc picture tronginc  路  3Comments

anyx picture anyx  路  3Comments

rafal-rudnicki picture rafal-rudnicki  路  3Comments

menme95 picture menme95  路  3Comments