← Recipe: call a server function

6. Replace src/main.luau

generated Luau client · trusted JavaScript handler · about 25 minutes
input → RequestId → resolve → decode → state

The application stores each request ID and changes visible state only after the matching completion is decoded.

When to use it

Replace the entry module after generation. This is the complete asynchronous client lifecycle rather than a synchronous function return.

Focused example

--!strict

local ServerFunctions = require("app/server-functions")
local UI = require("luastra/ui")

local App = require("luastra/app")
local Root = {}
local name = "Ada"
local status = "Ready"
local pending: {[number]: boolean} = {}

local function busy(): boolean
    return next(pending) ~= nil
end

function Root.render(): UI.Node
    return UI.Screen {
        id = "server-recipe",
        width = "content",
        padding = "responsive",
        UI.Card {
            id = "greeting/card",
            gap = "md",
            padding = "lg",
            surface = "elevated",
            UI.Text { id = "greeting/title", text = "Server greeting", variant = "title" },
            UI.Field {
                id = "greeting/name-field",
                gap = "xs",
                role = "group",
                label = "Name field",
                UI.Text { id = "greeting/name-label", text = "Name" },
                UI.TextInput {
                    id = "greeting/name",
                    label = "Name",
                    value = name,
                    onInput = "greeting.name",
                    autoComplete = "name",
                    enterKeyHint = "done",
                    required = true,
                },
            },
            UI.Button {
                id = "greeting/send",
                text = if busy() then "Waiting..." else "Ask the server",
                onTap = "greeting.send",
                disabled = busy(),
            },
            UI.Text { id = "greeting/status", text = status, role = "status" },
        },
    }
end

function Root.handle(action: string, target: string, value: string)
    if action == "greeting.name" and target == "greeting/name" then
        name = value
        status = "Ready"
    elseif action == "greeting.send" and target == "greeting/send" and not busy() then
        local requestId = ServerFunctions.greet({ name = name }, { deadlineMs = 2000, retry = true })
        pending[requestId] = true
        status = "Waiting for the server..."
    end
end

function Root.resolve(
    requestId: number,
    success: boolean,
    payload: string,
    errorCode: string,
    errorMessage: string
)
    if pending[requestId] ~= true then return end
    pending[requestId] = nil
    if not success then
        status = errorCode .. ": " .. errorMessage
        return
    end
    local result = ServerFunctions.decodeGreet(payload)
    if result == nil then
        status = "INTERNAL: Invalid greeting response"
        return
    end
    status = result.message
end

function Root.snapshot()
    return { name = name, status = status, busy = busy() }
end

return App.compose {
    render = function(_outputs) return Root.render() end,
    handle = Root.handle,
    resolve = Root.resolve,
    snapshot = Root.snapshot,
}

Complete inventory

View all pages in Recipe: call a server function
← Previous: 5. Generate the typed Luau clientNext: 7. Replace tests/smoke.luau →