A messenger arrives at the camp gate with a request: GET /legion/5. The server received it - but who is to handle it? The application has dozens of methods and none of them knows this one is for it.
In a legion, distributing orders is the centurion's job. He stands between the messenger and the soldiers: he takes the order, recognises whom it concerns, passes it on and sends back the answer. He does not do the work himself - he directs it. In NestJS that centurion is a controller.
A controller is a class with a decorator saying which stretch of routes it handles:
1@Controller('legion')
2export class LegionController { }The order is always the same:
, then @Controller('legion')
, and finally the class name with its body.export class
The
'legion' argument is a route prefix. Every method of this class will serve addresses beginning with /legion - and you do not have to repeat that on each of them. A controller with no argument, @Controller(), takes routes from the root.Inside the controller, each method gets a decorator saying which request it answers:
1@Controller('tributes')
2export class TributesController {
3 @Get()
4 findAll() { }
5
6 @Post()
7 create() { }
8
9 @Put(':id')
10 update() { }
11
12 @Delete(':id')
13 remove() { }
14}Four decorators match four actions on a resource.
fetches, @Get()
creates a new resource, @Post()
updates an existing one, @Put()
removes it.@Delete()
Telling
@Post from @Put tends to confuse, so remember them by what repetition does: sending the same @Post twice creates two resources, while sending the same @Put twice leaves one, merely written twice.The decorator's argument adds a piece of route.
@Put(':id') in a controller prefixed tributes will serve /tributes/42. The colon marks a parameter - a slot any value falls into.Since a route can contain a parameter, it has to be read somehow. Data arrives in a request by three roads, and each has its own decorator:
1@Get(':id')
2findOne(@Param('id') id: string) { }
3
4@Get('search')
5search(@Query('province') province: string) { }
6
7@Post()
8create(@Body() dto: CreateTributeDto) { }
pulls a value out of the address path - it is what reads @Param('id')
5 from /legion/5. @Query('province') takes a query parameter, what stands after the question mark: ?province=rome. @Body() reaches for the request body, sent with @Post and @Put.These three cover everything you usually need. Beware of two plausible-sounding names:
does not exist in NestJS - the path is served by @Path()
@Param. @Header() does exist but does something else: it sets a response header. To read request headers there is @Headers(), in the plural.Let's put it together. The order of a method's elements never varies:
1@Get()
2getAllLegionaries() {
3 return this.service.findAll();
4}First the decorator
@Get(), then the method name, then the body in braces, and inside it return this.service.findAll().And here you see what a controller really is. This method contains no logic - it takes the request and hands it to a service. A centurion does not forge swords; he knows which smith to send to.
This is a rule I recommend keeping from day one, @name: no database queries, calculations or business rules in a controller. When a controller method grows beyond a few lines, that is a sign it is doing something belonging to a service. The gain is practical - you will later call that same logic from a batch job or a queue consumer, where there is no HTTP at all.
Note that we do not build the response by hand. The returned value will be turned into JSON, and NestJS picks the status code itself: 200 for most methods, 201 for
@Post, because something was created.The centurion stands at the gate and knows whom to pass the order to:
@Controller('prefix'), export class, the class name,@Controller argument is a route prefix shared by every method of the class,@Get() fetches, @Post() creates a new resource, @Put() updates, @Delete() removes,@Post creates two resources, a repeated @Put leaves one,@Put(':id') serves /tributes/42,@Param('id') reads from the path, @Query('name') reads a query parameter after the ?, @Body() reads the request body,@Path() does not exist; @Header() sets a response header, and request headers are read by @Headers(),return this.service.findAll(),@Post.In the next lesson you will meet the ones the centurion passes orders to - services, where the real logic lives. For now remember: a controller recognises a request and names its executor - and every piece of data it needs arrives by one of three roads: the path, the query, or the body.