The Anatomy of an Iguana App

Back end Lua

A modern web app does not demand much of the server. All we need are convenient ways of serving up static files such as Javascript, CSS, and images, plus the ability to serve up data and request actions with JSON web services.

There’s nothing like looking at the code to understand how it works. To make it easier here, I have added many links to source code in our repository at GitHub. I also recommend installing the applications and using the translator’s code navigation and annotations. They help a lot with understanding how everything works.

We have a small Lua framework that makes it easy to serve up the Javascript, CSS, HTML and JSON web services. The framework is located in the module lib.webserver.

Have a look at couple of examples of how we use it:

Let’s go through the parameters.

The auth parameter is a boolean that defaults to false. If it’s true, the app require users to log in with a valid Iguana username and password.

The actions parameter points to a Lua table with functions that defines all the JSON web service calls the app uses. This is where URLs get mapped to backend functions. 

In the channel manager, the actions are defined at the end of the cm.app module. The bed monitor app has a much shorter list of actions defined in the bedmonitor.app module. Each of these actions (or URIs) maps to the name of a function that serves up JSON.

Each function receives a parsed HTTP transaction (which is a table produced by from net.http.parseRequest), and returns a Lua table. That table is then converted into JSON and sent back to the client.

For instance, the channel manager has an action called config_info, which is mapped to the function cm.app.configInfo.

If you have the Channel Manager application hosted on the base URL http://localhost:6544/chanman/, and you type http://localhost:6544/chanman/config_info into your browser’s address bar, you will see what the call returns:

This (looking at web service URLs with a browser) is a common technique to understand what each call is doing. It’s very useful to do during development.

The function is implemented really simply:

-- Lua Code
function cm.app.configInfo(Request)
   return {ExportPath = os.fs.name.toNative(cm.config.channelExportPath)}
end

There’s not much to it, really, which is the whole point. Each service returns information or performs an action on the server. To make a new service, you just create a new function, map it to a URI in the actions table, and you’re done. The fact that you can click through them in the translator editor with live annotations makes Iguana the easiest environment ever invented to write web services.

Now the ‘test’ parameter covers how we serve up HTML, Javascript and CSS files and tweak them easily. We have enhanced Iguana 5.6.6 so that you can edit these files when they are in the “other” folder like this:

That solves one problem, but still leaves us with the question of milestones. And that’s where the ‘test’ parameter comes in. By defining that parameter it means the files are pulled from the un-versioned sandbox under the Iguana working directory:

i.e. <Iguana Working Dir>/edit/<user name>/

That might be “C:\program files\interfaceware\iguana\edit\admin\”, depending on your operating system and what your Iguana user ID is.

So that solves the issue of milestones for the client side Javascript etc. code.  It makes for a very convenient workflow in which milestones don’t need to saved that often. Web services on the server side with Lua can be tested easily within the translator.  And the client side Javascript/CSS can be updated on the fly and tested in a browser.

Knowing how this works makes it easy to drop out to the command line or file explorer and use your own favourite editor when you need to. Personally, I’ll sometimes use Vim if I have certain types of editing tasks. Other times it’s more convenient to use the translator editor.

That’s it for the server side. Making these apps easy to build and easy to work with, without forcing our users to learn many new technologies, is one of our most important design goals.