Truly rapid development of admin apps with json-editor

At work we have a lot of configurable settings in our application. Like a lot. For a long time, nobody tackled the task of properly exposing the management of these settings in a UI because they thought it would be way too much work. The settings have a flat unified structure in the back-end, making them awkward to manage logically as a set of related settings. Building support for a large set of arbitrary data types, different editors for each, custom validation, etc. seemed like a daunting task.

In this blog post, I’m going to show how you can use the json-editor library to build these kinds of complex back-office admin apps really quickly and easily.

Configurable app settings management is often a neglected part of the product development process, typically cobbled together as a bare minimum effort. But it becomes very important as you scale an application up and the number of settings you have begin to inevitably increase. Without good administrative tools to both edit and check the correctness of settings updates, you can burden developers and chase issues with invalid data creeping into your system at runtime, rather than empowering the support and account management teams.

Using JSON schemas to model arbitrary data

JSON schema is a standard for encoding the structure of JSON data. It solves a number of problems associated with the domain of app settings management:

  • Naming things is hard. Settings can sometimes be given obtuse or confusing names. This is where titles & descriptions in JSON schema really come in handy. They allow you to provide human-readable names for your settings and provide documentation of the settings in the live system. Often wikis are created for this sort of thing, but they can quickly become out of date (or worse incorrect after changes). With settings schemas, it becomes part of your code, and will break if a setting name or data type change. This is enforced since this is the means with which the settings are updated.
  • The schema is just JSON. This makes it REALLY powerful and easy to use. You can freely manipulate the schema object in code, including transforming or augmenting it based on data you receive from the server-side, the user, etc.
  • You can group related settings into separate schemas. You prioritize which settings comes first, which are optional, etc.
  • You can base your schema on an ideal representation for editing, which may differ from the underlying storage representation. These editing representations can be made more intuitive, usable and less error-prone. You can then perform transformations to/from that representation, as opposed to forcing your users to contend with a potentially awkward internally persisted representation. This generally follows a simple pattern. You provide a pair of functions to transform in each direction, calling the appropriate function when fetching, and then right before sending the editor contents back to the server.

Enter json-editor

json-editor is a great little library. It builds on JSON schema and extends it in a number of useful ways. It essentially gives you auto-generation of web forms for free, complete with validation, built-in support for UI widgets, and useful hooks for interacting with it. Here’s a quick rundown of some of its features:

  • Full support for JSON schema version 3 and 4
  • It integrates with many popular CSS frameworks including Bootstrap so you can get a pretty form with zero effort
  • Integrates with a number of libraries that enhance the editor including WYSIWYG’s, icon libraries, and enhanced select and array widgets
  • Templating support
  • Watchers allow you to write custom callbacks when values in the editor change

A Simple Example

I wanted to put together an example that was complete enough to show what you can do with json-editor. In order to show a full end-to-end example, I decided to try out hyperdev. Hyperdev lets you get a live app running super fast, complete with client and server. It makes whipping up quick demos and proof of concept apps super easy and fun. You can see a live demo here. The code is also on github.

Imagine we have a multi-tenant SaaS company that offers a TODO application. Customers host their own instance of the TODO app tailored to their particular use case. The application has grown over time to have a number of clients and they have requested a number of things be configurable to meet their needs. Obviously this is a pretty contrived and simplistic example; it’s just meant to demonstrate some of the possibilities.

screen-shot-2016-09-10-at-12-06-19-pm

The Server

The server-side is super simple. It just has a set of get/post methods retrieving/updating settings for a given client. There’s also a get method for fetching the set of clients.

app.get("/settings", function (request, response) {
  var client = request.query.client;
  response.send(clientSettings[client]);
});

app.post("/settings", function (request, response) {
  var client = request.body.client;
  clientSettings[client] = JSON.parse(request.body.settings);
  response.send("Settings updated successfully.");
});

The settings store is just a simple in-memory map here, but obviously in a real application this would be stored in a database. In a real world application, this would probably be a REST API, which of course you could implement in any way you want.

var clientSettings = {
  "1": {
    "syncEnabled": true,
    "categories": "Work,Vacation",
    "defaultViewType": "brief",
    "voiceEnabled": false
  },
  "2": {
    "categories": "Home, Work, Personal",
    "defaultViewType": "detail",
    "syncEnabled": true,
    "voiceEnabled": true,
  },
  "3": {
    "defaultViewType": "brief",
    "syncEnabled": true,
    "voiceEnabled": false,
    "categories": "Work,Home,Travel"
  }
}

The Client

The client is actually where all the interesting code is. The client dynamically loads the settings for a given client into the schema form when selected from the clients dropdown.

screen-shot-2016-09-10-at-12-06-00-pm

In terms of the settings, I just created a few to show the variety and usage of the common data types (boolean, strings, enums, lists, etc.). Here’s an excerpt of a few settings properties:

"defaultViewType": {
  "type": "string",
  "title": "Default View Type",
  "description": "The default type of view to present to the user",
  "enum": ["", "detail", "brief"],
  "options": {
     "enum_titles": ["Select a default..", "Detailed View", "Brief View"]
   },
   "minLength": 1,
   "default": ""
},
  
"syncEnabled": {
   "type": "boolean",
   "format": "checkbox",
   "title": "Enable Sync",
   "description": "Enable TODO item sync with cloud server. Additional add-on module.",
   "default": false
}

The syncEnabled setting is a boolean property. The format type allows you to leverage built-in widgets in json-editor. In this case, we get a checkbox display for our setting in the form, but other formats are possible, for example, a simple dropdown. The defaultViewType property is an enumerated type and is a required field, as indicated by the minLength property.

A more interesting case is that of the categories setting.

screen-shot-2016-09-10-at-12-06-38-pm

Here, the back-end representation is not ideal for editing, as it’s a comma-separated list. What we want to display to the user is a list where they can easily add/remove elements from the list and do things like prevent duplicates in real-time. JSON schema has an array type for this with these properties. In order to match a schema using the array type, we’ll need to perform a simple transformation of the data we get back from the server to match our schema representation.

$.get('/settings?client=' + client, function(settings) {
  // transform to our schema representation
  settings.categories = settings.categories.split(",");
  editorOptions.startval = settings;
  editorContainer.empty();
  editor = new JSONEditor(editorContainer.get(0), editorOptions);
});

Likewise, when sending the updated settings object back to the server, we’ll need to transform the data back to the original form:

...

// Convert array of categories to server-side representation
updatedSettings.categories = updatedSettings.categories.join(",");

When submitting to the server, we can easily extract the current editor’s value with editor.getValue(), and validate that it conforms to the schema with editor.validate(). This will return an array of errors and their corresponding paths inside the schema, making presenting errors to users easy. There’s also an option to inline error messages as the user changes focus on fields, improving the user experience.

This comma-separated case might seem like a contrived example, but there are other more interesting cases with complex data types where the way the data is stored in the back-end doesn’t match up well with a desired format for editing. For example, if you have a set of closely related or constrained, but distinct settings (e.g. two lists that should be the same length) which should be edited or validated together, it can be simpler to assemble them into a single object which is edited/validated atomically.

Drawbacks

Af far as I’m aware, it isn’t super easy to customize json-editor’s look and feel, though it is possible. This is why I think it’s a great fit for back-office apps where look and feel are less critical, and it’s more about functionality. This doesn’t mean you can’t achieve nice UX with it if you think about it carefully. Simple things like pre-populating data as much as possible, making optimal use of enumerated types and good built-in documentation with titles and descriptions can really go a long way.

Conclusion

json-editor is a fantastic library. There are some other libraries out there that have similar capabilities but I haven’t found one that was as easy to use and well documented as JSON editor. Using it as a tool for back-office types of apps is really powerful and can help you create surprisingly complex and sophisticated apps fast. And in terms of complexity, it scales incredibly well. I’ve been able to quickly build editors suited to some pretty complex data types including tree structures and deeply nested maps among others.

Next time you need to built a back-office admin app, consider reaching for json-editor!

Leave a comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.