BCHS Logo

BCHS

JSON

BSD, C, httpd, SQLite

BCHS works especially well for JSON web applications, which is the de jure method for building dynamic web sites. These applications have a well-defined split between back-end (BCHS), transport protocol (JSON), and front-end (JavaScript, CSS, and HTML5). And there are plenty of tools available to build, document, and deploy your work. Why is this an improvement over simply emitting HTML?

  • server load: your user's browser is handling one request at a time—you're handling many, and the extra processing adds up
  • updates to site design don't require redeploying your application
  • reduced code-bloat from hard-coding complicated HTML trees
  • less work-flow dependency between back-end developer and front-end developer/designer
  • JSON is natively understood by JavaScript, which itself is a standard component of the web browser

Most (all?) complaints of JSON web applications fall to poor front-end decisions: using enormous frameworks, CSP violations, and really just plain shitty code. In this short document, I'll sketch out how to set up your BCHS environment for JSON applications. For a full working system using this method, see dblg.

Begin with the actual web application source. I use kcgi(3) and use pledge(2) (so it requires OpenBSD, of course) to safeguard our operating system. In this example, I just emit a simple greeting in a JSON container.

    1 #include <err.h>
    2 #include <stdarg.h>
    3 #include <stdint.h>
    4 #include <stdlib.h>
    5 #include <unistd.h>
    6 
    7 #include <kcgi.h>
    8 #include <kcgijson.h>
    9 
   10 int
   11 main(void)
   12 {
   13   struct kreq r;
   14   struct kjsonreq req;
   15   const char *page = "index";
   16 
   17   if (khttp_parse(&r, NULL, 0, &page, 1, 0) != KCGI_OK)
   18     return EXIT_FAILURE;
   19 
   20   if (pledge("stdio", NULL) == -1) 
   21     err(EXIT_FAILURE, "pledge");
   22 
   23   khttp_head(&r, kresps[KRESP_STATUS], 
   24     "%s", khttps[KHTTP_200]);
   25   khttp_head(&r, kresps[KRESP_CONTENT_TYPE], 
   26     "%s", kmimetypes[r.mime]);
   27   khttp_body(&r);
   28   kjson_open(&req, &r);
   29   kjson_obj_open(&req);
   30   kjson_putstringp(&req, "greeting", "hello, world");
   31   kjson_obj_close(&req);
   32   kjson_close(&req);
   33   khttp_free(&r);
   34   return EXIT_SUCCESS;
   35 }

In most large systems, seperate people work the front-end (HTML5, CSS, JS, etc.) and the back-end (BCHS). We can document the interface between this by using Swagger, which hooks into JSON Schema to document the input and output of each resource.

    1 {
    2  "swagger": "2.0",
    3  "info": {
    4   "title": "test",
    5   "description": "this is sample documentation",
    6   "contact": {
    7    "name": "Kristaps Dzonsons",
    8    "email": "kristaps@bsd.lv"
    9   },
   10   "version": "0.0.1"
   11  },
   12  "paths": {
   13   "/index.json" : {
   14    "get": {
   15     "description": "nice greeting",
   16     "produces": [ "application/json" ],
   17     "responses": {
   18      "200": {
   19        "description": "success",
   20        "schema": { 
   21         "type": "object",
   22         "properties": {
   23          "greeting": {
   24           "description": "a greeting",
   25           "type": "string"
   26          }
   27         },
   28         "required": [ "greeting" ]
   29        }
   30      }
   31     }
   32    }
   33   }
   34  }
   35 }

Front-end work! In this incredibly simple example, we use AJAX to asynchronously invoke our web application and put the result into an identified element. We use vanilla JS and pay deference to CSP by processing fully in the script. You'll obviously need to put your own application URL in the loader.

    1 <!DOCTYPE html>
    2 <html>
    3   <head>
    4     <meta charset="utf-8" /> 
    5     <title>sample</title>
    6     <script>
    7     function success(resp) {
    8       var res;
    9       try {
   10         res = JSON.parse(resp);
   11       } catch (error) {
   12         return;
   13       }
   14       document.getElementById('greeting').appendChild
   15         (document.createTextNode(res.greeting));
   16     }
   17     document.addEventListener('DOMContentLoaded', function() {
   18       var xmh = new XMLHttpRequest();
   19       xmh.onreadystatechange = function() {
   20         if (xmh.readyState === 4 && xmh.status === 200)
   21           success(xmh.responseText);
   22       };
   23       xmh.open('GET', '/cgi-bin/app/index.json', true);
   24       xmh.send(null);
   25     });
   26     </script>
   27   </head>
   28   <body>
   29     <span id="greeting"></span>
   30   </body>
   31 </html>