Skip to main content

Controllers

Understand how you can set up controllers in your REST API application.

Introduction

Instead of defining all of your request handling logic as closures in your route files, you may wish to organize this behavior using "controller" classes. Controllers can group related request handling logic into a single class. For example, a UserController class might handle all incoming requests related to users, including showing, creating, updating, and deleting users. By default, controllers are stored in the

Path.controllers()

./src/http/controllers

directory.

Writing controllers

Let's take a look at an example:

import { Context, Controller } from '@athenna/http'

@Controller()
export class UserController {
public async show({ request, response }: Context) {
return response.send(`User_${request.param('id')}`)
}
}

You can define a route to this controller method like so:

Route.controller('UserController').get('/user/:id', 'show')

// or

Route.get('/user/:id', 'UserController.show')

When an incoming request matches the specified route URL, the show method on the UserController class will be invoked and the route parameters will be passed to the method.

Resource controllers

If you think of each model in your application as a "resource", it is typical to perform the same sets of actions against each resource in your application. For example, imagine your application contains a Photo model and a Movie model. It is likely that users can create, read, update, or delete these resources.

Because of this common use case, Athenna resource routing assigns the typical creation, read, update, and delete ("CRUD") routes to a controller with a single line of code. To get started, we can use the make:controller command's --resource option to quickly create a controller to handle these actions:

node artisan make:controller PhotoController --resource

This command will generate a controller at

Path.controllers('PhotoController.ts')

./src/http/controllers/PhotoController.ts

. The controller will contain a method for each of the available resource operations. Next, you may register a resource route that points to the controller:

Route.resource('photos', 'PhotoController')

This single route declaration creates multiple routes to handle a variety of actions on the resource. The generated controller will already have methods stubbed for each of these actions. Remember, you can always get a quick overview of your application's routes by running the route:list command.

Partial resource routes

When declaring a resource route, you may specify a subset of actions the controller should handle instead of the full set of default actions:

Route.resource('photos', 'PhotoController').only(['index', 'show'])

Route.resource('photos', 'PhotoController').except(['store', 'update', 'destroy'])

Nested resources

Sometimes you may need to define routes to a nested resource. For example, a photo resource may have multiple comments that may be attached to the photo. To nest the resource controllers, you may use "dot" notation in your route declaration:

Route.resource('photos.comments', 'PhotoCommentController')

This route will register a nested resource that may be accessed with URLs like the following:

/photos/:photoId/comments/:commentId

Dependency injection and controllers

The Athenna service container is used to resolve all Athenna controllers. As a result, you are able to use any dependencies your controller may need using @Inject() annotation or in its constructor. The declared dependencies will automatically be resolved and injected into the controller instance when receiving a request from the server:

import { Inject } from '@athenna/ioc'
import { Controller, type Context } from '@athenna/http'
import { UserService } from '#src/services/UserService'
import { ProfileService } from '#src/services/ProfileService'

@Controller()
export class UserController {
private userService: UserService

@Inject() 👈
private profileService: ProfileService

public constructor(userService) { 👈
this.userService = userService
}

public async show({ response, params }: Context) {
//
}
}
tip

To learn more about the @Controller() annotation, visit the REST API annotations documentation page.