Routing-controllers: fix: TypeDI integration not working as expected on docs

Created on 12 Jan 2021  路  7Comments  路  Source: typestack/routing-controllers

Description

Cannot use TypeDI as explained on the README for basic services.

Obviously, it is written that it only requires the @Service on service side (seems legit), but this actually doesn't work without telling @Service before the controller as well.

Minimal code-snippet showcasing the problem

Controller snippet

import { Service } from 'typedi'

import { Get, JsonController, Param, State } from 'routing-controllers'
import { CollaboratorService } from '../services/CollaboratorService'

@JsonController('/service/:serviceId/collaborator')
@Service() // This shouldn't be required as described on the README, but it is actually required to run the app as expected
export class CollaboratorController {
  constructor(private collaboratorService: CollaboratorService) { }

  @Get('/')
  async getCollaborators (@State('user') user: User, @Param('serviceId') serviceId: number) {
    return await this.collaboratorService.getCollaborators(
      user.id,
      serviceId
    )
  }

Actual service snippet

import { Service } from 'typedi'

@Service()
export class CollaboratorService {
  async getCollaborators (userId: number, serviceId: number) {
    ...
  }
}

Expected behavior


I've imported everything and created the DI Container before the app, the Controller should be working without the @Service decorator declaration.

Actual behavior


It doesn't, if I try to run the code without the @Service decorator on top of the Controller, I'll get the following error:

{
    "name": "ServiceNotFoundError",
    "message": "Service with \"MaybeConstructable<CollaboratorService>\" identifier was not found in the container. Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.",
    "stack": "ServiceNotFoundError: Service with \"MaybeConstructable<CollaboratorService>\" identifier was not found in the container. Register it before usage via explicitly calling the \"Container.set\" function or using the \"@Service()\" decorator.\n    at ContainerInstance.get (L:\\...\\node_modules\\typedi\\cjs\\container-instance.class.js:45:15)\n    at Object.value (L:\\...\\node_modules\\typedi\\cjs\\decorators\\inject.decorator.js:31:42)\n    at L:\\...\\node_modules\\typedi\\cjs\\container-instance.class.js:286:58\n    at Array.forEach (<anonymous>)\n    at ContainerInstance.applyPropertyHandlers (L:\\...\\node_modules\\typedi\\cjs\\container-instance.class.js:280:46)\n    at ContainerInstance.getServiceValue (L:\\...\\node_modules\\typedi\\cjs\\container-instance.class.js:240:18)\n    at ContainerInstance.get (L:\\...\\node_modules\\typedi\\cjs\\container-instance.class.js:29:25)\n    at Function.get (L:\\...\\node_modules\\typedi\\cjs\\container.class.js:28:36)\n    at Object.getFromContainer (L:\\...\\node_modules\\routing-controllers\\container.js:40:42)\n    at ControllerMetadata.getInstance (L:\\...\\node_modules\\routing-controllers\\metadata\\ControllerMetadata.js:26:28)",
    "normalizedIdentifier": "MaybeConstructable<CollaboratorService>"
}

I don't know if this is an upstream issue or not.

needs docs wontfix fix

Most helpful comment

Hi!

This is the expected behavior. Since TypeDI 0.9.0 it won't create instances for unknown classes, so you need to decorate your classes. This needs to be documented and also I believe a fix is possible as routing-controller can auto-register them in its own decrator.

All 7 comments

Hi!

This is the expected behavior. Since TypeDI 0.9.0 it won't create instances for unknown classes, so you need to decorate your classes. This needs to be documented and also I believe a fix is possible as routing-controller can auto-register them in its own decrator.

Hey

Thanks for the answer, I'll make a pull request tonight to add some documentation about this !

I hate to ask, but: how does something like this sound when the developer says it out loud?

  • Why should I have to mark all participating classes as services?
  • Why does typedi break dependent libraries on "illegal" instantiation? If this is a preference, rather than a behavior critical to the pattern, shouldn't it warn and gracefully return undefined, so that I decide if the missing dependency is critical to me or not?

A meme for the ages.

Services everywhere.

Still no solution yet for not having to put @Service() on all classes?

Hi, I have the same problem now, how do I solve it?

/Users/yacovets/Documents/GitHub/Chat-react-socket.io-nodejs/node_modules/src/container-instance.class.ts:75
    throw new ServiceNotFoundError(identifier);
          ^
ServiceNotFoundError: Service with "MaybeConstructable<ErrorHandlerMiddleware>" identifier was not found in the container. Register it before usage via explicitly calling the "Container.set" function or using the "@Service()" decorator.
    at ContainerInstance.get (/Users/yacovets/Documents/GitHub/Chat-react-socket.io-nodejs/node_modules/src/container-instance.class.ts:75:11)

NodeJs: 16.0.0
"typedi": "^0.10.0"
"reflect-metadata": "^0.1.13"
"routing-controllers": "^0.9.0"

index.ts

import 'reflect-metadata'
import { useExpressServer, useContainer } from 'routing-controllers'
import { Container } from 'typedi'

import './util/env'
import Server from './core'
import socket from './socket'
import * as controller from './controller'
import { ErrorHandlerMiddleware, ErrorNotFoundMiddleware } from './middleware'
import './service'

useContainer(Container)

const server: Server = new Server()
socket(server.getIo())

useExpressServer(server.getApp(), {
    routePrefix: '/v1',
    controllers: controller.v1,
    defaultErrorHandler: false,
    middlewares: [
        ErrorHandlerMiddleware,
        ErrorNotFoundMiddleware
    ]
})

server.listen()

export default server

controller.ts

import { Response } from 'express'
import { JsonController, Post, Req, Res, UseBefore } from 'routing-controllers'
import ModifiedRequest from '../../interface/ModifiedRequest'
import { CheckAuthorizationMiddleware } from '../../middleware/CheckAuthorizationMiddleware'
import {UserService} from "../../service/UserService";
import {Service} from "typedi";

@JsonController()
@Service()
@UseBefore(CheckAuthorizationMiddleware)
export default class User {

    constructor(private userService: UserService) {}

    @Post('/profile')
    getProfile(@Req() request: ModifiedRequest, @Res() response: Response) {
        return this.userService.getProfile()
    }

}

service.ts

import { Service } from 'typedi'

@Service()
export class UserService {

    getProfile() {
        return {
            ok: true
        }
    }

}

@jenya-yacovets Please take some time to investigate before asking someone to do your job. 馃槈

It seems like your ErrorHandlerMiddleware "Service" isn't found in Container, but since you didn't provide any part of this code, I can just guess that you forgot adding the @Service() decorator in front of your middleware !

@jenya-yacovets Please take some time to investigate before asking someone to do your job. 馃槈

It seems like your ErrorHandlerMiddleware "Service" isn't found in Container, but since you didn't provide any part of this code, I can just guess that you forgot adding the @Service() decorator in front of your middleware !

Thanks! I thought that only in controllers you need to specify @Service()

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ghost picture ghost  路  5Comments

hellraisercenobit picture hellraisercenobit  路  4Comments

OysteinAmundsen picture OysteinAmundsen  路  3Comments

codedoge picture codedoge  路  6Comments

davidquintard picture davidquintard  路  4Comments