Skip to main content
The @talosjs/controller component is the HTTP/WebSocket handler layer. A controller is a class with a single index method, bound to a route by a @Route decorator. When a request matches the route, the framework constructs a context (request data, response builder, logger, cache, user, locale, and route metadata) and calls index(context). The handler reads what it needs from context and returns a response. Generics carry your params, payload, queries, and response shapes end to end, so the context is fully typed.

The model

Every controller implements IController as a single typed index(context) handler. There is no base class to extend and no boilerplate. IController<T> and ContextType<T> thread your params, payload, queries, and response types through the whole handler, and the handler receives a lot in that context: the parsed request, a response builder, the logger, cache, rate limiter, locale, authenticated user, headers, files, IP, host, and the matched route metadata. Routing is decorator-bound. @Route.get, @Route.post, @Route.socket, and the rest tie a class to a path, method, version, validation schema, and roles in one place. The index method may return an IResponse directly or a Promise<IResponse>; both work. The same controller shape covers HTTP and WebSocket, with socket controllers using a context that adds a channel API.

How it works

A request flows through routing into the matched controller and back out as a response. The handler never constructs the context; the framework builds it, then calls index. The index signature is the whole contract:
ContextConfigType describes the shape you bind to a route. response is always present, while params, payload, and queries come from the request config and are optional. ContextType<T> is the object passed to index. Its most-used members:

Usage

A controller is a class with one index method, decorated with a @Route method matching the HTTP verb. Define a route type once and pass it to both the decorator config and ContextType<T> so the context is fully typed.
The @Route object exposes one decorator per HTTP method — get, post, put, patch, delete, options, head — plus socket for WebSocket controllers. Each takes the route path and a config: A controller with URL params reads them through context.request.params() (validated by the decorator’s params):
Mutation routes read the body from context.request.payload():
The context exposes the authenticated user, logger, and locale directly, so read only what the handler needs:

Socket controllers

WebSocket controllers share the same one-method shape but are bound with @Route.socket and use ContextType from @talosjs/socket, which adds a channel API (channel.send, channel.publish, channel.subscribe, channel.close, channel.ws). The CLI scaffolds either variant; pass --is-socket=true for the socket template.

Keeping controllers clean

Keep controllers thin. Validate and shape the request, hand the work to an injected service, and return the response; no business logic lives in index. Define one route type and reuse it, passing the same type to the @Route config and to ContextType<T> so params, payload, queries, and response stay in sync. Include only what the route needs: params for path segments, payload for post, put, and patch, queries for list and search endpoints, and response every time. Read request data through context.request.params(), .payload(), and .queries() so the decorator’s Assert schemas run before your code does. Set roles to the lowest role that satisfies the endpoint, since access is hierarchical and a role also grants the ones it inherits. Return responses through the context.response builder rather than assembling raw HTTP responses by hand. Finally, treat cache, rateLimiter, permission, and user as optional: they may be absent, so guard with ?. and have a fallback.

CLI command

Scaffold a controller, its route type, and a test file with the generator. It writes the class to modules/<module>/src/controllers/<Name>Controller.ts, registers it in the module, and installs @talosjs/controller if it is missing.
The generated class is a typed stub: a route type, a @Route decorator with empty Assert schemas, and an index that returns an empty JSON response, ready for you to fill in:
See controller:create for the full command reference.

Use with Claude and Codex

The generator ships a matching controller:create skill. It runs the scaffold and then guides your AI agent through completing the controller: filling in the route type, validation schemas, roles, and the index handler, and delegating logic to a service. Initialize the skills once for your agent:
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the implementation:
Prompt
For example, the prompt above maps to a controller:create run with an inferred name, route, and method, then implements the index handler against the user’s profile service.