Skip to content

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.

ts
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.

ts
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 or number, 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 a context object, which contains any data you want to send to the server.

ts
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.

Released under the Apache-2.0 License.