Concepts: Controllers (Core)
In this section we shall discuss the concept of a controller within the Sodacore framework.
Introduction
A controller is a class (or collection) of methods that will handle an incoming event, and dispatch a response.
Think of a controller as a location for your business logic, now when I say business logic, I don't mean that you should put all your code in here, but rather the logic that is specific to the request being handled, Providers are a great place to put reusable logic that can be used across multiple controllers or even just normal classes, but in general, think of a controller that follows the MVC pattern, where the controller essentially is your entry and exit point.
Within the core package alone, the only concept is a controller for threads, however in other plugins, like HTTP, WebSockets and more, the concept stays the same, but the request and response type changes.
As of right now, the controllers for
threads
aren't documented, this is because the API is going to change and the whole threads concept will re-evaluated, as workers are the same thing, but better.
HTTP Controllers
Within the HTTP plugin, controllers, noted above, are collections of methods that handle incoming HTTP responses and return responses.
Controllers are decorated with the @Controller
decorator, which takes a base path as an argument, this base path is then prepended to all routes within the controller.
import { Controller, Get } from '@sodacore/http';
@Controller('/users')
export class UserController {
@Get('/')
async getUsers() {
// ...
}
}
In the above example, we have a controller that handles requests to the /users
endpoint, and within that controller, we have a method that handles GET
requests to the /
endpoint, which in this case would be /users
.
As of right now, the HTTP package implements the following method decorators:
@Get
@Post
@Put
@Delete
@Patch
@Options
Each of these decorators takes a path as an argument, which is then appended to the base path of the controller.
Paths can be static, or they can be dynamic, using the :
syntax, and additionally, a path can be optional by appending a ?:
to the end of the path segment.
import { Controller, Get } from '@sodacore/http';
@Controller('/users')
export class UserController {
@Get('/:id')
public async getUser(@Params('id') id: string) {
// ...
}
@Get('/logout/?:session')
public async logout(@Params('session') session?: string) {
// ...
}
}
Useful Notes
As the Sodacore framework wants to try and avoid as much useless boilerplate as possible, the methods you write have some useful defaults when returning data.
When you return a value we will automatically convert it to a Response
object for you, using the following rules:
- If
Response
, returns as-is, so you still have full control. - If
Error
, returns a 500 response with the error message. - If
null
, returns a 404 response. - If
string
ornumber
, returns a 200 response with the value as the body. - If
object
, returns a 200 response with a JSON body. - If
true
, returns a 201 response. - If
false
, returns a 400 response. - If
undefined
, returns a 204 response.
WebSocket Controllers
Within the WebSocket plugin, controllers are collections of methods that handle incoming WebSocket messages and may return responses.
Controllers are decorated with the same @Controller
decorator, but instead are imported from the @sodacore/ws
package and take a namespace string as an argument, this namespace is then prepended to all events within the controller. All methods are then exposed using the @Expose
decorator.
In the WebSocket plugin, the way routing works is you send a structured JSON object that contains a
command
which is<namespace>:<method>
and acontext
object, which contains any data you want to send to the server.
import { Controller, Expose, WsContext } from '@sodacore/ws';
@Controller('chat')
export class ChatController {
@Expose()
public async message(ctx: WsContext) {
// Handle incoming WebSocket message.
}
}
As you can see we define a controller, with a namespace of chat
, we then expose a method called message
, which will handle incoming WebSocket messages with the command chat:message
, WsContext is a helper class that allows you to easily access the underlying WebSocket connection, as well as any data that was sent with the message.
Useful notes
- All methods must be decorated with
@Expose
to be registered. - Any data returned will be sent back to the client, in the exact same format.