Don't install v25.15.5, it's a broken version of titan
The Titan CLI command is now titan.
The previous tit command is still supported as a legacy alias.
titan planet

Actions

Execute request-aware business logic in JavaScript, compiled and embedded into Titan’s native Rust server.

What is an Action?

An action is a JavaScript function that executes when a route is matched.
Actions receive the full request context and are responsible for all dynamic behavior in a Titan application.

Routes define what endpoint exists.
Actions define what happens when that endpoint is called.

app/actions/hello.js
export default function hello(req) {
  return { ok: true }
}

When to use an Action

Use an action whenever you need:

  • Access to request data (params, query, body, headers)
  • Conditional logic or validation
  • Database or external service calls
  • Dynamic response generation

Static responses should use reply() instead.


Connecting a Route to an Action

Routes reference actions by name.

app.js
t.get("/user/:id<number>").action("getUser")

Titan automatically resolves this to: app/actions/getUser.js

No imports or wiring are required.


Action file structure

Actions live in the app/actions directory.

app.js
getUser.js
login.js
search.js

Each file exports a single function.


Accessing route parameters (req.params)

Typed route parameters are validated before the action runs.

app/actions/getUser.js
export function getUser(req) {
  return {
    userId: req.params.id,
  }
}
  • req.params.id is guaranteed to match the declared type
  • Invalid parameters never reach the action

Reading query parameters (req.query)

Query strings are parsed automatically.

app/actions/search.js
export function search(req) {
  return {
    q: req.query.q,
  }
}

Request:

GET /search?q=titan

Result:

{ "q": "titan" }

Reading request bodies (req.body)

Titan parses request bodies based on content type.

app/actions/login.js
export function login(req) {
  return {
    email: req.body.email,
  }
}
  • JSON bodies are parsed automatically
  • Invalid payloads are rejected before execution

Returning responses

Actions return plain JavaScript values.

app/actions/example.js
export function example(req) {
  return {
    success: true,
  }
}

Titan automatically:

  • Serializes objects to JSON
  • Sets appropriate response headers
  • Sends the response from native Rust

Returning HTTP errors

Throwing an error signals a failed request.

export function protectedAction(req) {
  if (!req.headers.authorization) {
    throw new Error("Unauthorized")
  }

  return { ok: true }
}

Titan converts errors into proper HTTP responses or log on the terminal.


Synchronous and deterministic execution

Actions execute synchronously inside Titan’s embedded JavaScript engine.

  • No Node.js runtime
  • No event loop
  • No background async side effects

This ensures predictable execution and easy reasoning about behavior.


Separation of responsibilities

LayerResponsibility
RoutesURL structure and HTTP methods
ActionsRequest handling and logic
Rust serverExecution, routing, and performance

This separation keeps applications clean, testable, and maintainable.


Mental model

Routes describe structure Actions handle behavior Titan compiles both into Rust

This model scales cleanly from small APIs to large production systems.

On this page