Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(question) How to inherit exposed controller http methods? #228

Closed
cojack opened this issue Nov 4, 2017 · 20 comments
Closed

(question) How to inherit exposed controller http methods? #228

cojack opened this issue Nov 4, 2017 · 20 comments

Comments

@cojack
Copy link
Contributor

cojack commented Nov 4, 2017

Hi,
It's possible to inherit parent controller methods? I tried to create kind of RestController with CRUD methods, but the child seems to not get any of them.

Any idea?

@cojack cojack changed the title How to inherit exposed controller http methods? (question) How to inherit exposed controller http methods? Nov 4, 2017
@wbhob
Copy link
Contributor

wbhob commented Nov 5, 2017

There is no such thing as a "parent controller" in Nest; you can't extend another controller with the expectation of inheriting the routes. Can you share your use case?

@kamilmysliwiec
Copy link
Member

Hi @cojack,
Example would be helpful 🙂

@cojack
Copy link
Contributor Author

cojack commented Nov 5, 2017

export class RestController {
  @Get('/')
  async findAll() {
    return [];
  }
}
@Controller('/someurl')
export class SomeController extends RestController {
}

Some url for me should have @get at /someurl but it doesn't. Any idea how to implement this kind of thing?

@mkzaker
Copy link

mkzaker commented Nov 11, 2017

Hi
I need this feature ,too.

@MonsieurMan
Copy link

MonsieurMan commented Nov 14, 2017

Seems to me that it would be really hard to have a clue which routes ends up in the extending controller.
By the way why do you want to do such a thing ?
Maybe an interface would be a better option but I don't know if it would be really reusable.

@wbhob
Copy link
Contributor

wbhob commented Nov 14, 2017

No, interfaces would not be reusable. Really, by nature controllers should not be extendable. My recommendation would be to put the functionality in a component, and reuse it in your controllers.

@kamilmysliwiec
Copy link
Member

Hi 🙂
I'm also not convinced to make controllers extending possibly. Hope you'll forgive me @cojack 🙂

@jgj
Copy link

jgj commented Nov 28, 2017

I have this exact use case and was surprised to find it didn't work. A more concrete example (Using TypeORM):

export abstract class EntityController<T> {
    constructor(protected readonly service: EntityService<T>) { }

    @Get()
    async findAll() {
        return this.service.findAll();
    }

    @Get(':id')
    async getById(@Param() params) {
        return this.service.getById(params.id);
    }

    @Post()
    async create(@Body() data: Partial<T>) {
        return this.service.create(data);
    }
}

Every single one of my TypeORM entities should expose basically the same restful CRUD interface and, using generics, be type safe about the underlying Entity model. I've already done this with the service that wraps the typeorm repository (EntityService). An extending controller looks like:

// ThingService extends EntityService<Thing>

@ApiUseTags('things')
@Controller('things')
export class ThingController extends EntityController<Thing> {
    constructor(private readonly thingService: ThingService) { super(thingService) }
}

This seems like a perfectly valid pattern to me, and one which will save me tons of boilerplate/copy and pasting. None of the essential CRUD logic differs between any of my entities. And, if it did, the most appropriate place to handle it would be the Service or Middleware/Interceptors/Pipes/Guards, not the controller. The value of REST is that the interface is based on the shape of the state being transferred and a set of known verbs. The verbs don't change between controllers and the shape of the state is determined by the generic type argument.

If there is a limitation or difficulty in supporting this pattern I understand not being willing to support it. But to say there isn't a valid use case is unfair.

@kamilmysliwiec
Copy link
Member

This feature is available since v4.6.0 🎉

@cojack
Copy link
Contributor Author

cojack commented Feb 20, 2018

@kamilmysliwiec HELL YEAH! You don't even know how much work u've saved for me!

@omar
Copy link

omar commented Mar 5, 2018

Thanks for getting this in, but I'm running into an issue where the Swagger UI isn't picking up the model objects for each endpoint. Making requests works fine, but the Swagger UI shows nothing under the "Example Value" and "Model" in the documentation.

Is this related to this issue nestjs/swagger#41 or am I just misconfiguring the generic controller?

@jmls
Copy link

jmls commented May 26, 2018

@jgj, @omar do you have an example project of using generics , nestjs and typeorm ? I'm wanting to ditch loopback and are evaluating various frameworks

@lixaotec
Copy link

Dear @kamilmysliwiec, @cojack could you provide some sample on that?

@bitflut
Copy link

bitflut commented Jul 28, 2018

Controller inheritance does not work for me using v5.1.0.

Routes are correctly mapped to function calls, but any other decorator is omitted. Is this expected behavior or am I doing something wrong here?

export class ParentController<T> {

    constructor(public service: Service) {}

    @Patch('/:id')
    @UseInterceptors(Interceptor1, Interceptor2)
    public update(@Body() dto: UpdateDto): Promise<T> {
        return this.service.update(dto);
    }

}

@Controller('my-route')
export class ChildController extends ParentController<ChildResource> {

    constructor(private _service: ChildService) {
        super(_service);
    }

}

If I call PATCH /my-route/:id, then the interceptors are never called, but the update function is executed.

@bitflut
Copy link

bitflut commented Jul 29, 2018

This could possibly be fixed in #907. I will let you know when I've tested it :-)

@bitflut
Copy link

bitflut commented Jul 29, 2018

I can confirm that #907 fixes my controller inheritance issue thx 👍 👍

@dmayerdesign
Copy link

dmayerdesign commented Dec 3, 2018

First of all, amazing library. Blew my mind when I learned of it at AngularMix and I'm now having a blast playing with it.

Is the expectation that, given

export class EntityController<EntityType> {
    protected abstract _repository: Repository<EntityType>

    @Get()
    public async list() {
        return this._repository.list()
    }
    
    // [...]
}

I should be able to create

@Controller('foo')
export class FooController extends EntityController<Foo> {
    constructor(
        @Inject(FooRepository) protected _repository: Repository<Foo>,
        @Inject(SomeService) private _someService: SomeService
    ) { super() }

    @Get()
    public async list() {
        return this._someService.specialList()
    }
}

and the @Get() metadata from EntityController#list will be safely overridden?

@iofedurin
Copy link

@kamilmysliwiec in same case like @bitflut's example i'm trying to create default CRUD controller for my entities. And want to ask about dto validation.

My version:

crud.controller.ts:

export class CRUDController<T extends BaseEntity, DTO extends DeepPartial<T>> {
  constructor(protected readonly crudService: CRUDService<T>) {}

  @Post()
  public create(@Body() data: DTO): Promise<T> {
    return this.crudService.create(data);
  }
}

example.controller.ts

@Controller('example')
export class ExampleController extends CRUDController<ExampleEntity, ExampleDto> {
  constructor(protected readonly exampleService: ExampleService) {
    super(exampleService);
  }
}

In this case i want to use generic type DTO for validation but like i understood it needs to be a class, not type.
Is there any way to validate request body?

@murbanowicz
Copy link

@iofedurin have you found the solution to pass the DTO?
I am struggling with this right now and so far no solution.

@lock
Copy link

lock bot commented Sep 23, 2019

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.

@lock lock bot locked as resolved and limited conversation to collaborators Sep 23, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests