JavaScript-based Web UI

Overview

This tutorial will show you how to implement a plain JavaScript-based user interface (UI) over a REST-based backend.

We'll concentrate here on the basics of the UI, for the basics of implementing a RESTful API and usage of the underlying Mongoose function calls, please check the REST basics tutorial.

Build and try

  • Follow the Build Tools tutorial to setup your development environment.

  • Start a terminal in the project directory; if you've not already done so, clone the Mongoose Library repo

    git clone https://github.com/cesanta/mongoose
    
  • Build and run the example

    cd mongoose/examples/webui-plain
    make clean all
    
  • Go to http://localhost:8000 in your browser; the JavaScript code loads and executes, obtaining a configuration and rendering the page; see the results in the log

config load
  • Change any parameter, it will be sent to the backend. You can also re-send any one of the values by clicking its "Update" button. Note that, since we don't have any update mechanism in place, yet, to get any possible configuration changes made by other users, you have to click the "Get configuration" button
config change
  • The "Get configuration" button sends an HTTP request using the GET method to the URI /api/config/get and updates the page, we can simulate that manually by connecting to the REST API with curl, for example:

    curl http://localhost:8000/api/config/get
    {"url":"mqtt://broker.hivemq.com","pub":"mg/my_device","sub":"mg/#"}
    
  • Changes in an input or clicking on one of the "Update" buttons send an HTTP request using the POST method to the URI /api/config/set, containing a JSON object with the changed parameter in the BODY. We can simulate that manually by connecting to the REST API with curl, for example:

    curl http://localhost:8000/api/config/set -d '{"url":"mqtt://otherbroker.some.where"}'
    
  • Let's verify that by requesting the configuration again:

    curl http://localhost:8000/api/config/get
    {"url":"mqtt://otherbroker.some.where","pub":"mg/my_device","sub":"mg/#"}
    

Backend implementation

  • When the event manager is initialized and our server starts up, we create a handy copy of pointers to the configuration values. This will allow us to just change the pointers later on, when we get a config change request
  • When we get an HTTP request for the /api/config/get URI, we serve a response using mg_http_reply(), taking advantage of the identifier m and, through the macro MG_ESC(), function mg_print_esc(), to print double-quoted JSON-escaped strings.
  • When we get an HTTP request for the /api/config/set URI, we parse the JSON object in the POST body for each configuration element. The body element inside the struct mg_http_message we got, is a struct mg_str; we pass this to our update_config()function. Finally, we just print the required HTTP OK result
  • The function mg_json_get_str() will parse a JSON string in a struct mg_str, searching for a string value at the JSON path path. If successful, it will return a pointer to it, so we free our former config value and generate a new one.

Check the JSON-RPC tutorial for a more involved example of handling JSON.

Frontend implementation

The JavaScript code in the browser gets the elements that are present on the page, then tries to get the configuration, calling the fetch() method to GET /api/config/get and extracting the returned JSON object. If successful, updates the values and enables the buttons

  • Each HTML input and button is configured to call its corresponding update function
  • Each update function takes the value present in the input and calls update(), that calls the fetch() method to POST to /api/config/set sending its arguments in the body as a stringified JSON object