Stateless autogenerated admin panels

Dmitrii Ragozin
11 min readJun 17, 2021

Is it possible to create content management interfaces with zero time development? Yes, it is possible. The article will show how.

Issues to solve

  1. It is difficult for a user to operate a backend microservice API directly without a web interface.
    At the moment a lot of companies have a great variety of backend microservices and their main goal is to provide programming interfaces to get data access. Such interfaces are used in companies for proper functioning of web-products and web-services (for example, online stores, bank websites, information resources, etc.). In addition to product development microservices are used to solve back office issues with content management being one of the main problems to solve. The consumers of this option are the employees of a company such as content editors, content managers, product managers, etc. These groups of users have limited expertise, therefore using an API directly leads to negative user experience. A content management interface has to be intuitively comprehensive for users which will make it possible to decrease the amount of errors and speed up working with the content.
  2. Content management interfaces development for every backend microservice is a costly process.
    Apart from developing a company’s product or service there is also a demand to create content management interfaces. In most cases such interface development requires extra time for frontend developers. This time is needed to create and maintain interfaces, to ensure the stability of work, to fix bugs, etc. Considering that any company can have a great number of backend microservices, creating a separate admin panel for each of them is known to be very expensive.

Task setting

To solve the above mentioned issues we can try to carry out the following task: why don’t we enable any backend microservice to create a content management interface with zero time development.

Content management using a stateful admin panel service

Content management using a stateful admin panel service.

To solve this kind of issues we can consider using a stateful admin panel service as a management interface. As a result we can get the following points:

  • We need to develop backend, which will obtain the data.
    As a stateful system is used, we will use the storage provided by this system. Therefore we will have to develop backend which will bring data from the system to the microservice.
  • Extra resources to provide the operation of the stateful system.
    To maintain stable functioning of the stateful system we need some extra resources to store data and cope with the loading.
  • Limited possibilities for data validation.
    The stateful system has limited possibilities for data validation or it cannot be consistent with the microservice. So it is necessary to validate the available information additionally at the microservice level. We can get validation inconsistency and in case the microservice considers the entered information invalid, with the stateful system finding it valid, the user will not know about it.
  • Extra point of failure.
    Moreover the stateful system can be unavailable, so data updates may not be received.

Let’s look at another solution which does not have these weak points.

A stateless solution suitable for any backend microservice

What if we try and implement a universal admin panel suitable for any backend microservice? For this we will need the following:

  1. Loose coupling.
    This means lack or minimisation of knowledge about each other between the microservice and the admin panel. As our goal is to create a universal admin panel, it cannot depend on the peculiarities, business logic or the implementation of one particular microservice.
  2. Abstract data models.
    We reduced the coupling between the admin panel and the microservice so we cannot know in advance what data structures we will have to work with, therefore we have to use abstract data models.
  3. Interface generation at runtime.
    The universal admin panel does not know beforehand what backend it is going to work with. It will only be able to receive the data structures of the microservice and build the user interface after accessing the microservice. So we need runtime interface generation.
  4. The information exchange protocol.
    Well, and the last thing, as the admin panel does not store the knowledge about the data structures of the microservice, about endpoints from where the data can be retrieved, we need a protocol according to which the admin panel will receive such information.

It seems to be a challenging problem to solve from scratch. Let’s try and search for some available solutions.

React Admin

The first thing to consider is React Admin that provides a set of templates to create content management interfaces.

  • It provides a great variety of ready-made features such as input validation, i18n, optimistic ui. It also makes it possible to work with different kinds of backend (rest, graphql, soap…), etc.
  • It uses the entity-relationship model to store data. Originally the project was named Admin-on-rest because the project was built with the use of REST protocol. REST protocol has left its mark on the project implementation. The data in the application are split into resources (entity types in the ER model), relationships between resources and resource elements (entities in the ER model).
  • The project is being actively developed and maintained. The solution has a component structure. There is a possibility of customisation and scaling.
  • To implement admin panels on the React Admin basis the development on the frontend side is needed. Unfortunately it conflicts with our task requirement to create an admin panel with zero time development.

API Platform Admin

The next solution to consider is API Platform Admin.

  • The solution is based on the React Admin which provides all the advantages and possibilities of this solution. Like React Admin, it has a modular structure, which allows to scale and customise the system to meet the customers’ needs.
  • To reduce the coupling between the backend and the interface the solution uses JSON-LD and Hydra Core Vocabulary specifications. Using these specifications the interface has a possibility to receive the information about the types of system entities, the fields of every entity, the list of operations and relationships between the entities. The received information makes it possible to generate the interface at runtime.
  • When it comes to backends which support JSON-LD and Hydra Core Vocabulary, frontend development is not required to create admin panels based on the API Platform Admin.
  • API Platform Admin meets our task requirements. Let’s take a closer look at it.

Loose coupling specifications

There are some specifications which can be used to reduce the coupling between the backend and the frontend.

Current APIs heavily rely on out-of-band information such as human-readable documentation and API-specific SDKs. However, this only allows for very simple and brittle clients that are hardcoded against specific APIs.

The Hydra Core Vocabulary specification defines the vocabulary which allows machines to understand how to interact with an API. Some special attributes appear in the response, the elements of the vocabulary which explicitly denote what type of information the server provides. For example, the server uses such attributes to send data collections, lists of resources or operations with every resource, etc.

  • HAL.
    The next specification is HAL — Hypertext Application Language. It allows to describe relationships between system resources using hyperlinks. According to HAL specification:

HAL is a little bit like HTML for machines. The difference is that HTML has features for helping ‘human actors’ move through a web application to achieve their goals, whereas HAL is intended for helping ‘automated actors’ move through a web API to achieve their goals.

  • OpenApi (Swagger).
    We can consider one more specification, OpenApi (or Swagger), which allows to reduce the coupling between the backend and the frontend. I believe that many people reading this article used the Swagger interface and have worked with the OpenApi specification. OpenApi specification is used to describe the available API of a system. It allows to describe a list of system methods, the results of methods execution, data models, input parameters, relationships between data. As Swagger is quite a popular tool I will tell you in detail how to build an autogenerated admin panel based on the OpenApi specification.

OpenAPI (Swagger)

OpenAPI allows to describe existing system API. But it does not provide an opportunity defining a list of system resources and operations on them for automatically generating of an admin interface. Therefore it is needed some extra information about the way of building a list of resources and getting possible operations for every resource on the Swagger schema basis. This kind of information can be obtained on the basis of special agreements. For example using the tags of operations for resource list forming or special naming for each resource. Or, as in the example below, for naming the operationId field use a special template for every operation, where you can get the name of the resource and a type of the operation. On the schema basis your team can use their own conventions for generating the list of resources and operations on them.

The example of using Swagger

Let’s take a look at an example of creating an autogenerated admin panel based on Swagger. We are going to create a web application that provides an opportunity to manage pets: get a list of pets, add and edit pet information. The application backend is using mock data. The complete source code for the application can be found here.

As I mentioned above OpenApi does not provide an opportunity defining a list of system resources and operations on them. So the following convention is going to be used: an operation type and a resource name will be stored in the operationId field of the Swagger schema. So the operationId field will be composed according to the following template: ${operationType}_${resourceName}, where operationType is the type of the operation executing on the resourceName resource. Possible types of operations: get_list — getting a list of records, get_one — getting one record, etc. The full list of operations you can find here. In our example the operationId field of getting a list of pets looks like this.

Let’s take a look at how this admin panel works.

  • We can see how the Swagger schema was loaded (./swagger-pet.json).
  • Next, using our naming convention, we are generating the list of service resources. We can see the /pet/all operation. The value of operationId field of this operation is get_list_pet. Therefore the application understands that this operation is used for getting a list of entities in the Pet resource. We can also see the related data model: Pet.
  • Next, we are getting a list of fields for this resource based on our data model. For the Pet model these resource fields are: id, name, breed.
  • Also we can see the operation get_one (to get detailed information about an entity), and create (to add an entity).
  • Further we can see the item corresponding to the resource appears in the left menu of the interface.
  • Let’s try to get detailed information about the entity. In this case we can see that the endpoint related to the get_one operation was invoked.

Other resources work the same way.

System modules

Fundamentally, our admin panel schema can be divided into the following blocks:

  1. UI template is a set of ui components that build our interface. We use react-admin for this.
  2. Admin guesser is a module responsible for runtime generation of our interface. The generation occurs on the base of the existing data schema, possible operations and links between the data.
  3. Schema parser is a module that parses the data schema and reports all information about the system data structure. We use parseSwaggerDocumentation for this from the @api-platform/api-doc-parser package. Different parsers can be used for different needs. For example, GraphQL schema parser or HAL schema parser. You can also write your own parser which meets your requirements.
  4. Data Provider is a service that performs API operations. In our example we are using swagger-client. It make it simple working with API endpoints. It allows to work with the endpoints without worrying the way they are implemented under the hood. You do not need to think about the method the endpoint uses: GET, POST, DELETE. How data and parameters are going to be passed — in the body of the operation or in the query. It is enough to pass the operation id, the parameters and the data we want to send. Swagger-client is going to do the operation and return the result.

Schema change

Let’s have a look at automatic resource adding when schema changing.

We are adding a new field in our data model: category which stores a pet category (for example, cat, dog, guinea pig, etc.). We just need to add this field to our schema like this to be able to work with this field in the interface. After that we can see how the corresponding column appears in the list of records without any development. This column shows us pet category.

Similarly the field appeared for create, edit and detail view operations.

Usage areas

As the main usage areas for this approach we consider the following:

  • Admin panels;
  • Autogenerated personal area;
  • Configuration parameter management of site pages such as page titles, site block visibility, block order, etc;
  • Seo site settings;
  • and many others.

This approach is not applicable:

  • In case of tight coupling between backend and frontend. If there is some custom logic this approach is not for you. Perhaps, it is cheaper to develop an admin panel from scratch.
  • Abstract models limit a lot of possibilities of the interface. The public area of the site (i.e. https://www.facebook.com/) is more focused on usability, rather than development speed. So it would be difficult to create it based on auto generation . Thus it is hardly possible to use this approach for the public area of the site.

Summary

At the beginning of the article we talked about what we need for autogenerated admin panels. Let’s recap how we have coped with our points.

  1. Loose coupling. The interface knows nothing in advance about microservice resources, data structure, models and relationships between models. The requirement is met.
  2. Abstract data models. The implementation of our interface is based on the E-R model, which is an abstract data model. The point is completed.
  3. Interface generation at runtime. We saw the way our interface generation occurs at runtime. The point is completed.
  4. Information exchange protocol. To get information about the system data structure we use the OpenApi protocol and an additional naming convention. The point is completed.

Thus the task we set ourselves, namely: “to provide any backend microservice with the possibility of creating a content management interface with zero time development” is fulfilled.

Resources

--

--