Cell - MVC Component
Controllers for Cell backends: classes with @Controller whose methods answer HTTP requests. It runs on @celljs/web (node:http, no express). Guide: HTTP APIs.
npm install @celljs/mvc @celljs/express-adapter@celljs/express-adapter is the backend entry that starts the server; the cell init templates already depend on both.
import { Body, Controller, Get, Param, Post } from '@celljs/mvc/lib/node';
@Controller('users')
export class UserController {
@Get(':id')
get(@Param('id') id: number) {
return { id }; // JSON
}
@Post()
create(@Body() user: { name: string }) {
return user;
}
}Import the controller's file from the DI module (src/node/module.ts), or it is not loaded.
Routes
| Decorator | |
|---|---|
@Controller(path?) | A component (singleton, proxied for AOP) whose methods are routes under path. Also takes ComponentOptions: @Controller({ path: 'users', rebind: true }) |
@Get, @Post, @Put, @Patch, @Delete, @Head, @Options | One HTTP method; the argument is the path under the controller (':id'), empty for the controller path |
@All(path?) | Every method |
Path syntax: :name is one segment, * matches anything, (...) is optional. Routes are mounted under server.path (default /); mvc.path (default /*) is the pattern of requests the MVC handler takes. A request that matches no route gets 404 with the method and path in the message.
Parameters
| Decorator | Value |
|---|---|
@Param(name?) | A path parameter, or all of them |
@Query(name?) | A query parameter, or the whole parsed query |
@Body(name?) | A field of the parsed body, or the whole body |
@Header(name?) | A request header, or all headers |
@Cookie(name?) | A request cookie |
@Session(name?) | A session value |
@Req() / @Res() | The request / response objects of @celljs/web |
Parameters typed String, Number, Boolean or Date are converted. Parameters typed with a class are validated when @celljs/validation is installed.
Responses
| Decorator | Response |
|---|---|
none or @Json() | The return value as JSON (mvc.defaultViewName: json) |
@Text() | The return value as text/plain |
@Html(template?) | A Mustache template rendered with the return value (see below) |
@File(file?) | A Buffer or stream returned by the method, or the file file in mvc.baseFileDir (default assets, relative to the directory of the server code) |
@View(name, metadata?) | A view by name; add views by binding View |
@Header(name, value) | Sets a response header |
@Cookie(name, value) | Sets a response cookie |
@Session(name, value) | Stores a value in the session |
A method may be async. Returning undefined answers 200 with an empty body. Set another status on the response (@Res() res, res.statusCode = 201) and return the body as usual.
mvc.jsonView.template wraps every JSON response and error in a template, for APIs that answer { "success": ..., "data": ... }: {{model}} (JSON, null when there is none), {{error}}, {{success}} and {{code}} are replaced, every occurrence; {{error}} and {{code}} are escaped for a JSON string, so write them inside quotes.
Errors
Throw HttpError(status, message) or a subclass such as NotFoundError from @celljs/web/lib/node to answer with that status; other errors answer 500. Stack traces are not sent. The error handlers of @celljs/web answer with the message ErrorMessageResolver allows: in production a server error gives only its status text (web.errors.exposeMessages changes it). With mvc.jsonView.template, JsonViewErrorHandler (priority 2000) answers every error with the template instead and keeps the status of the response.
@Catch(ErrorType | ErrorType[]) on a method of the controller handles the errors of those types thrown by its routes; the method gets the error and its return value is the response:
@Catch(QuotaError)
@Text()
onQuota(err: QuotaError) {
return `handled: ${err.message}`;
}Application-wide error handling is an ErrorHandler of @celljs/web.
HTML views
@Html('page.mustache') renders a template from mvc.mustache.baseViewDir (default assets/views) with the value the method returns. mvc.mustache.cache: true keeps compiled templates in memory.
Templates use the Mustache syntax, without a third-party engine:
{{name}}writes a value HTML-escaped,{{{name}}}or{{& name}}as it is;user.namereads a path,.is the current item;{{#list}}...{{/list}}repeats for each item, enters an object, or shows when the value is truthy;{{^list}}...{{/list}}shows when it is falsy or empty;{{! comment }}writes nothing.
Partials ({{> name}}) and other delimiters ({{=<% %>=}}) are not supported: such a template fails when it is compiled.