Titan Planet Logo

Actions

Execute request-aware business logic in JavaScript, TypeScript (Beta), or Rust, compiled and embedded into TitanPl’s native server.

What is an Action?

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

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

TitanPl is unique because it allows you to write endpoints in JavaScript, TypeScript (Beta), and Rust within the same project.


🔵 TypeScript Actions (.ts)

BETA

Fully typed, strict, and auto-compiled. TitanPl enforces zero type errors before running.

app/actions/hello.ts

import { defineAction } from "../../titan/titan";

interface HelloResponse {
    message: string;
    user_name: string;
}

// "defineAction" provides automatic type inference for "req"
export const hello = defineAction((req): HelloResponse => {
    t.log("Handling request with strict types...");

    return { 
        message: "Hello from TypeScript!",
        user_name: req.body.name || "Guest"
    };
});

Strict Type Safety

If titan dev detects a type error, the server will not run. This ensures you never ship or test broken code.


🟡 JavaScript Actions (.js)

STANDARD

Perfect for business logic, rapid prototyping, and IO-bound tasks.

app/actions/hello.js
export function run(req) {
    t.log("Handling user request...");
    return { 
        message: "Hello from JavaScript!",
        user_id: req.params.id 
    };
}

🔴 Rust Actions (.rs)

BETA

Perfect for heavy computation, encryption, image processing, or low-level system access.

Note: The Native Rust Action API is currently in Beta.

app/actions/compute.rs
use axum::{response::{IntoResponse, Json}, http::Request, body::Body};
use serde_json::json;

pub async fn run(req: Request<Body>) -> impl IntoResponse {
    // Perform heavy computation here
    t.log("Processed 1M records in Rust");
    Json(json!({ "result": "Processed 1M records in Rust" }))
}

⚡ Hybrid Action System

TitanPl automatically detects, compiles, and routes all types.

  • .ts files are type-checked and compiled.
  • .js files are bundled.
  • .rs files are compiled into the native binary.
  • All share the same routes.json configuration.
ModeStatusDescription
JavaScript✅ StableStandard JS development.
TypeScript⚠️ BetaStrict typing usage.
Rust + JS⚠️ BetaHybrid runtime with Rust actions.
Rust + TS⚠️ BetaHybrid runtime with Rust and TS actions.

⚙️ Enabling Rust Actions

To enable Rust support in an existing Titan project:

  1. Update your CLI to the latest version:

    npm install -g @ezetgalaxy/titan
  2. Update package.json to include the rust template:

    {
      "titan": {
        "template": "rust"
      }
    }
  3. Run the update command to generate the necessary native files:

    titan update

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.ts
  • app/actions/getUser.js
  • app/actions/getUser.rs

No imports or wiring are required.

Naming Convention Warning

You MUST ensure the following match exactly:

  1. The file name in app/actions (e.g., getUser.ts)
  2. The route reference in app.js (e.g., .action("getUser"))

Titan uses this convention to automatically wire your application. If they do not match, the action will not be found.


Action file structure

Actions live in the app/actions directory.

app.js
getUser.ts
compute.rs
login.js

Each file exports a single function. In JavaScript/TypeScript, it's the run or default export. In Rust, it's the run function.


Accessing route parameters (req.params)

Typed route parameters are validated before the action runs.

app/actions/getUser.ts
export function run(req: Context) {
  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 run(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.ts
export function run(req: Context) {
  const { email } = req.body as { email: string };
  return { email };
}
  • JSON bodies are parsed automatically
  • Invalid payloads are rejected before execution

Returning responses

Actions return plain JavaScript objects (JS/TS) or IntoResponse (Rust).

app/actions/example.js
export function run(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.


Powered by multi-threaded Gravity (JS)

JavaScript actions execute synchronously within the Gravity runtime, a high-performance multi-threaded engine embedded into the native Rust server.

  • Multi-threaded Execution: Scaling across all CPU cores.
  • No Node.js Runtime: Zero overhead from traditional event loops.
  • Predictable Execution: Linear, easy-to-reason logic via Drift.

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