Wednesday, October 18, 2017

Why I both hate and love the swagger-ui?

This post is a critique on swagger-ui and a specific react/redux pattern implemented over there.

Before you proceed, you may need to have some knowledge of the following:

React/Redux

Swagger UI

Open API Specification, Loopback

OAS v3.0 (Open API Specification) is out. I was thinking of writing this post long ago when OAS was still 2.0. Now that team is probably migrating to support the latest specification.
However, the scene at present is that most frameworks like loopback, still generate swagger.json as per OAS v2.0. Therefore the exercises I am about to show you might still convey the point.

The most negative aspect that sticks out is that the UI is simply too slow for large datasets.

This is actually a very complex array-reduce operation. It is very well abstracted in this line, but bear with me. The initial load of the page (with the api info populated) is slowed down by the aforesaid operation. That is the slowness for the initial loading. But there is another aspect of its slowness.

React Reconciliation Process

The other cause for slowdown is react's reconciliation process which becomes apparent for large datasets. Any change in the redux state, triggers this reconciliation. React will do what its designed for - diff the current dom state with the virtual dom and only change those parts which have changed.

Sometimes the UI would freeze when you, say, toggle a tag. (A tag is synonymous to a model or a table in a database. All basic CRUD operations of that model will be grouped under that tag). With React, I believe the DOM manipulations are minimal - only the stuff that is changed changes. It is the diff computation that turns out to be expensive; and you cannot avoid it. Normally.

There are strategies to avoid it. One way to do so is that, we maintain the tag as a traditional react component which is disconnected from the redux store.
Enough said. This is all theory. You probably want to manually verify this.
There are two ways about it.

If you have some service that generates the swagger.json (and webserver allows for CORS) you can plug that in directly to the petstore app and preview your apis. You will need a large swagger.json for this to convince yourself.

If you don't have a large dataset you can generate one using oeCloud. And then use the swagger.json generated there, in the petstore app for experimentation.

I am going to follow the 2nd approach.

oeCloud is a loopback based framework which is geared to build enterprise grade applications. Its takes an API-first approach for fleshing everything out. On top of loopback, it supports personalization, eventual consistency, dynamic model creation or runtime model authoring, etc.

Post installation you might want to run the following two commands in succession in the project directory:$ node . -m$ node .

The first command establishes some models required by the oecloud framework. The second command starts the server.
You might see a message saying 'Browse your REST API at http://localhost:3000/explorer'

Note: make sure your port 3000 is available for this to work right away. You can configure this though.

Step 1 - Loading your API in petstore

What we want from oe-cloud is the following url:

http://localhost:3000/explorer/swagger.json

Next we visit the petstore swagger app. Plug this url in the petstore app.

Step 2 - Observing the slowness

Come down to ModelDefinition part - the first operation is a POST. Click it.

Notice that there was a slight freeze in UI responsiveness between that click and the subsequent UI state.

Alternatively we can do this in the developer console. (Just redo step 1, and continue with the following line).

...and hit Enter.
This will do the same effect as above.
We can see the delay either way. You may probably want to dig deeper at this point. But a fair warning, the code base isn't friendly for those who are not initiated to react/redux.

But what does swagger-ui do right?

Yes, the slowness is a bum. But I had the opportunity to work with that code base and learn a few patterns for structuring a react/redux project. It is mainly their plugin architecture.

This enables you to extend the swagger-ui application, write your own components, and make each component aware of the system. The "system" in this context is analogus to a mechanic's toolbox.
As you write your own plugins you probably write your own components, some utility functions, etc. If you expose them they finally land in the toolbox. You can even re-use them in the react components you create and the pre-existing ones.

The system has a factory method to load a component by its name. This is very useful for composing your react components.

Better design of your application state

Each stateplugin your end up writing can either wraps something of an exiting state, extends it, or, creates a new state branch. Each state branch you create has its own actions, reducers, and selectors. When the application bootstraps you have neat namespaces via which you can access them.

For e.g. recall what was done in the browser console above.

For oeCloud framework, we had taken this code base and adapted to our needs. We have added code to handle our specific authentication needs. We also tweaked some performance issues that were part of the original swagger-ui project. You can reach that project here.Warning: there are some minor bugs at this moment. If you happen to come across one please to raise an issue over there. Remember the original project was modified for oeCloud specific use. It may not be able to execute your APIs properly. But we can guide you nonetheless.
Links: