Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

HTTP Server

Forge includes a built-in HTTP server powered by axum and tokio. Servers are defined declaratively using decorators and launched automatically when the interpreter detects a @server directive.

Architecture

Forge Source
    |
    v
Parser extracts decorators (@server, @get, @post, ...)
    |
    v
runtime/server.rs builds axum Router
    |
    v
axum + tokio serve requests
    |
    v
Each request locks the Interpreter mutex,
calls the handler function, serializes the return value as JSON

The server implementation lives in src/runtime/server.rs (~354 lines).

Server Configuration

The @server decorator configures the server:

@server(port: 3000, host: "0.0.0.0")
ParameterTypeDefaultDescription
portint8080Listen port
hoststring“127.0.0.1”Bind address

Route Decorators

@get(path?)

Registers a function as a GET handler.

@get("/users")
fn list_users() {
    return [{ name: "Alice" }, { name: "Bob" }]
}

@post(path?)

Registers a function as a POST handler.

@post("/users")
fn create_user(body) {
    say "Creating: " + body.name
    return { ok: true }
}

@put(path?)

Registers a function as a PUT handler.

@delete(path?)

Registers a function as a DELETE handler.

@ws(path?)

Registers a function as a WebSocket handler. The function receives each incoming message as a string and returns a response string.

@ws("/chat")
fn handle_message(msg) {
    return "Echo: " + msg
}

Path Parameters

Path parameters use colon syntax (:param). They are automatically mapped to function parameters by name.

@get("/users/:id")
fn get_user(id) {
    return { id: id, name: "User " + id }
}
// GET /users/42 -> { id: "42", name: "User 42" }

The Forge path syntax (:id) is internally converted to axum’s brace syntax ({id}).

Handler Parameters

Handler functions receive arguments based on their parameter names:

Parameter NameSource
Name matching a path paramURL path parameter
body or dataParsed JSON request body
query or qsQuery string as an object
Name matching a query paramIndividual query parameter
Othernull
@post("/search")
fn search(body, query) {
    say "Search body: " + str(body)
    say "Query params: " + str(query)
    return { results: [] }
}

JSON Serialization

Return values from handlers are automatically serialized to JSON:

Forge TypeJSON
intnumber
floatnumber
boolboolean
stringstring
nullnull
arrayarray
objectobject
ResultOk(v){"Ok": v}
ResultErr(v){"Err": v}
Other"<TypeName>"

Error Handling

If a handler function throws a runtime error, the server returns HTTP 500 with:

{ "error": "error message here" }

CORS

CORS is enabled by default with a permissive policy (all origins, all methods, all headers). This is suitable for development; production deployments should add appropriate restrictions at the reverse proxy level.

Concurrency Model

The interpreter is wrapped in an Arc<Mutex<Interpreter>>. Each incoming request acquires the mutex lock, calls the handler, and releases it. This means handlers execute serially – only one request is processed at a time.

For high-throughput scenarios, consider running multiple Forge server instances behind a load balancer.