Chapter 24: Routes, Controllers, And Responses

Part IV: MVC, Data, Auth, Forms, and AI

Why this chapter matters

Routes turn HTTP requests into controller actions. This chapter teaches how requests become data and how controller methods return responses.

What you will build

You will build a small MVC route table and one controller that returns views, plain text, JSON, redirects, status codes, and headers. The example stays inside a sample app so route inspection and project checks are repeatable.

Concepts in plain English

A route matches an HTTP method and path, then calls a controller action. The action returns a response.

The chapter uses these concepts:

Vocabulary and commands

Primary coverage: GET, POST, PUT, PATCH, DELETE, route, view, text, json, redirect, status, header, and println for local diagnostics while developing actions.

Guided example

Open examples/learn/23-mvc/controllers. Its route file maps HTTP verbs and paths to controller/action pairs:

codeGET "/" ResponsesController "index" route
GET "/ping" ResponsesController "ping" route
GET "/api/status" ResponsesController "status_json" route
GET "/old-dashboard" ResponsesController "legacy" route
POST "/messages" ResponsesController "create" route
PUT "/messages/:id" ResponsesController "update" route
PATCH "/messages/:id" ResponsesController "patch" route
DELETE "/messages/:id" ResponsesController "destroy" route

The receiver still comes before the selector-like word. A route declaration reads as: method, path, controller, action, then route.

Inspect the route table:

coderco routes examples/learn/23-mvc/controllers

The output should include every route in this order:

codeGET / ResponsesController#index
GET /ping ResponsesController#ping
GET /api/status ResponsesController#status_json
GET /old-dashboard ResponsesController#legacy
POST /messages ResponsesController#create
PUT /messages/:id ResponsesController#update
PATCH /messages/:id ResponsesController#patch
DELETE /messages/:id ResponsesController#destroy

The index action renders an escaped view through the request context:

code[
  "Routes And Responses" title var
  "Response helpers keep each action result explicit." summary var
  $ctx
  "responses/index" swap view
] "index" Method

Text responses are values first, then response modifiers:

code[
  "ping requested" println
  "pong" text
  201 status
  "x-ricochet" "learn" header
] "ping" Method

json wraps any Ricochet value that can be serialized:

code[
  statusBody map
  $statusBody "ok" true put drop
  $statusBody "service" "learn" put drop
  $statusBody "routes" 8 put drop
  $statusBody json
] "status_json" Method

Redirects are also explicit action results:

code[
  "/api/status" redirect
] "legacy" Method

Declared arguments bind path and request data before the block runs. Because Ricochet is stack-based, bind them from the top of the stack:

code( id title body ) [
  body var
  title var
  id var

  updated map
  $updated "id" $id put drop
  $updated "title" $title put drop
  $updated "body" $body put drop
  $updated json
] "update" Method

Run the project doctor after editing controllers or routes:

coderco doctor examples/learn/23-mvc/controllers

How to read the example

Read the MVC example in layers. First identify the route or command that enters the app. Then find the controller action. Then follow the data into the response, view, model, or database boundary. Each layer is still ordinary Ricochet: values first, words second, and explicit results at boundaries.

Try it

Add a route for a health check:

codeGET "/health" ResponsesController "health" route

Then add an action:

code[
  health map
  $health "ok" true put drop
  $health json
] "health" Method

Run rco routes again and confirm /health appears before serving the app.

Check your understanding

Common mistakes

Safety notes

The example only inspects routes and compiles the app. It does not start a long-running server or create local generated state. When you do serve it, bind to 127.0.0.1 for local development unless you have a reason to expose it.

Production guidance

Production controllers should validate input, keep response shapes predictable, and avoid doing persistence, rendering, and authorization work in one large method. Let route names stay boring and obvious.

What you know now

You know how a Ricochet route reaches a controller action, how action helpers turn ordinary values into web responses, and how declared arguments let request data enter the stack without breaking the postfix shape.

Next step

Continue to Chapter 25: Templates, Static Assets, And Uploads.