REST, RESTful, AJAX, and RPC

REST, RESTful, AJAX, and RPC share a common foundation: they all involve remote calls between client and server. At their core, these techniques are patterns for making remote procedure calls (RPC) from a web page (the browser) to a server, and back again.

This quick introduction shows how to use modern JavaScript (Fetch API + async/await) to send URL-encoded data and receive JSON responses from a Lua Server Page (LSP). You'll learn how to build a dynamic web page that communicates with the server without forcing a full-page reload. However, we recommend the following tutorials for deeper learning:

REST / AJAX : Quick Introduction

An Ajax web application retrieves data from the server asynchronously in the background without interfering with the display and behavior of the existing page. Ajax applications are typically designed for enhancing standard web-applications and are designed to gracefully degrade to a standard web application when the browser does not support JavaScript or if JavaScript is disabled.

In the following example, a basic calculator is designed as a standard web application, and we will later enhance this calculator by using Ajax. To keep it simple, the calculator will be designed to add two numbers only.

<?lsp

   local var1
   local var2
   local result = ""
   
   -- Fetch all URL encoded (POST'ed form) data sent from client as a Lua table.
   local d = request:data()
   if d.var1 and d.var2 then -- If client sends data
      -- Convert URL encoded data to numbers
      var1 = tonumber(d.var1)
      var2 = tonumber(d.var2)
      -- var1 and/or var2 are nil if data could not be converted to a number.
      -- Set result = var1+var2 if var1 and var2 are OK, otherwise set result to
      -- "???"
      result = var1 and var2 and (var1+var2) or "???"
   end
   -- Else, a standard browser GET
   
?>
<html>
  <head>
    <title>Addition</title>
  </head>
  <body>
    <form method="post">
      <input type="text" name="var1" value="<?lsp=var1 or ""?>"/>
      +
      <input type="text" name="var2" value="<?lsp=var2 or ""?>"/>
      = <span id="result"><?lsp=result?></span>
      <br/>
      <input type="submit" value="Submit"/>
    </form>
  </body>
</html>

The html form starts on line 26 and ends on line 33. The user enters a number into the two text fields (line 27 and 29). The data is sent as URL encoded data to the server when the user presses the submit button on line 32. The URL-encoded data for the above form is sent as: var1=10&var2=11, where 10 and 11 are numbers entered into the text fields.

Lua script code starting on line 1 and ending on line 20 is executed by the server every time the browser sends a request to the LSP page. If the browser sends the two URL-encoded variables var1 and var2, line 9 to 17 in the above code is executed on the server. The server side code extracts the variables and converts the variables to numbers.

The "result" is dynamically inserted into the generated HTML on line 30. We also update the value attributes on line 27 and 29. Updating the value fields make the input variables persistent -- i.e. the HTML code returned in response to a "browser POST request" have the text fields set to the previous value.

Lets's go ahead and enhance the web-application using Ajax, but at the same time, make sure the application still works when JavaScript is disabled in the browser.

<?lsp
local var1
local var2
local result = ""

-- Fetch URL-encoded form data (GET or POST)
local d = request:data()
if d.var1 and d.var2 then
   var1 = tonumber(d.var1)
   var2 = tonumber(d.var2)
   result = var1 and var2 and (var1 + var2) or "???"

   -- Handle AJAX request
   if "true" == request:header"X-AJAX" then
      trace"AJAX call"
      response:json{result = result} -- does not return
   end
   trace"Standard POST"
end
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Lua Server Pages - AJAX Example</title>
  <script>
    async function calculateAsync() {
      const var1 = document.getElementById("var1").value;
      const var2 = document.getElementById("var2").value;

      const formData = new URLSearchParams();
      formData.append("var1", var1);
      formData.append("var2", var2);

      try {
        const response = await fetch("index.lsp", {
          method: "POST",
          headers: {
            "Content-Type": "application/x-www-form-urlencoded",
            "X-AJAX": "true"
          },
          body: formData
        });

        if (!response.ok) throw new Error("Network response was not OK");

        const data = await response.json();
        document.getElementById("result").textContent = data.result;
      } catch (error) {
        console.error("Error:", error);
        document.getElementById("result").textContent = "???";
      }
    }
  </script>
</head>
<body>
  <h1>Lua Server Pages - AJAX Example</h1>
  <form action="index.lsp" method="post">
    <input id="var1" name="var1" type="text" value="<?lsp= var1 or "" ?>" />
    +
    <input id="var2" name="var2" type="text" value="<?lsp= var2 or "" ?>" />
    =
    <span id="result"><?lsp= result ?></span>
    <br><br>
    <input type="submit" value="Standard POST" />
    <input type="button" value="AJAX" onclick="calculateAsync()" />
  </form>
</body>
</html>