Your APIs never die, they simply turn into OpenAPI specification definitions
Background
Xiana is a ground for our solutions and it already is in our daily lives. In Flexiana, we, professionals, love the tools that change our perspective. Xiana is our new focus because we shape it and it touches our perspectives instantly. This closed loop is so meaningful since we believe in the purity of functional approach on both design and implementation. Xiana, as a Framework, provides the best on Routing
, it brings our best approach combined with Reitit
routes. Our hand-tailored process of generating our routes with Reitit
routes covers almost every feature it already has. In this specific problem, it needs to be more than just writing our routes as Reitit
tells us.
The Problem
Best to try a good, top-down approach to this. So, we can meet with the terms;
SwaggerUI
Visualization is another reducing process we are using a lot both to simplify and clarify. SwaggerUI takes your OpenAPI specification definitions of your routes and renders them, reducing them to simple end-points that explain themselves with docstring
, parameters
, and so on. It targets browsers and has good documentation built in.
We need that as a route in order to provide a complementary solution. Serving the browser content to render our routes as we want is a part of the problem.
You can try it online here to see the magic of it.
OpenAPI
In 2015, the OpenAPI Specification was donated to the Linux Foundation as part of the OpenAPI Initiative. By effectively mapping all of the resources and operations associated with an API, the specification creates a RESTful interface for easily developing and consuming it.
Since SwaggerUI
consumes OpenAPI specification definitions, we need to turn our routes into them. This transformation is another part of the problem.
Xiana’s Custom Routes
Now, Xiana is providing a successor approach to Reitit routes
‘s handlers. Action
s are basically yet another function for managing our flow of processing from the request up to the response. It contains all your business logic in it and wraps them well.
We need our custom routes as Reitit
. So we can have some fidelity to the original approach of Reitit
. This brings us the functionality we need and cover our backs with the community-centered library implementations.
The Solution
We already defined our problem and split it into multiple, manageable pieces. And we can follow the solution as the same structure as the problem.
SwaggerUI
We inspected SwaggerUI, first. It’s well-designed software written in Javascript. Like much highly-demanded software in the Web Industry, it already provides clean documentation and CDN for external/explicit usage of it. These two were important because our goal is to provide an integrated solution with Xiana.
As the documentation states, we embrace the configurable parts and integrate with Xiana. This is important. In way of getting a fluent experience with Xiana, we should provide some data to the SwaggerUI so it can create auto-magic for us. Like the URI of the OpenAPI specification definitions, we are going to serve with that configuration, no user needs to place the URI of it by hand.
We implemented the route of SwaggerUI in Xiana with the Clojure way of web component writing, Hiccup
. It already fits in well.
We provide it at the configuration level, by design. In that design choice, we aim to provide Xiana users with less code-surface to manage by hand and many options that they can control easily with configurations in the notation they already love, EDN.
OpenAPI
We inspected Reitit’s original implementation for its routes to generate such data to feed into SwaggerUI, and we saw that API is clear and the code is so clean. In that part of the solution, we provide a customized flow of calculations for our custom process in Xiana.
Chapter-1
Calculation of the routes into the definitions of the specification is our first chapter. We process them first as we discussed because our routes in Xiana are successors. We need to align them first. We implement that functionality by inspecting both of them carefully. Thanks to the Xiana
and Reitit
documentations, it’s clear to take a clear view of the differences between them.
Chapter-2
In the second chapter, we just inspect the original implementation of generating the OpenAPI specifications in Reitit
. And we saw that only a couple of tweaks can match our requirements to gain this functionality. Tweaks like providing additional information into the compilation process of routes so the routes can generate more pieces of information and eliminates some unwanted middleware-informations. We also implemented it as a bridge function. Our calculation is not returning Ring Response
.
Chapter-3
After the calculation of the aligned routes based on Reitit, we can feed it to the tweaked version of the transformer implementation that generated OpenAPI specification definitions from our routes and convert it into JSON because the SwaggerUI is expecting it in the notation of JS objects. So the end value sits there in our state
.
Xiana’s Custom Routes
And we gathered a good solution that contains the implementation’s calls, configuration data and a trick to inject Swagger-related routes into the application routes implicitly.
We simply generate Swagger UI end-point to serve HTML content of it with a bit of help of CDN content of Swagger UI at there.
The Result
The resulting usage of the feature is just a single Xiana
system function. We can pass it like any other one. The only limitation is we need to place it before the xiana.route/reset
, because that system function alters the routes, and compiles them with Reitit
. You may ask; We could still fetch the original routes by a simple extraction from compiled Reitit routes itself. The trick starts with our implementation looking for some meta
of the routes themselves for keeping the structure of Xiana
routes as close as possible Xiana
way.
For example, if you wanna ignore some routes at the end of Swagger UI
, you can simply mark them with :no-doc
flag. It’s a natural flag from Reitit implementation and we extend it to wrap our structure with meta, too. We just create another type of data injection with meta. Placing the data where it belongs just before processing them into OpenAPI specification definitions.
A naive example;
^:no-doc ["/assets/*" (ring/create-resource-handler {:path "/"}))]
This route will be ignored while processing your routes for Swagger UI.
Example
If you desire to write your own application with Xiana as we do in Flexiana. You can use the Xiana template for that. The template already has the implementation for the integration.
# creating the application:
lein new xiana swagger-with-xiana
# changing the directory to it:
swagger-with-xiana
# Running Docker-backed DB:
docker-compose up -d
# starting the repl
lein repl
# Changing the `ns` and run the System
(ns user)
(start-dev-system)
# go to the web browser and observe;
open "<http://localhost:3000/swagger/swagger-ui>"
Conclusion
While we were aiming to bring the integration of Swagger to Xiana
, we knew that it would give something back to us in the way of perspective on our routing design choices. The alignment of ours and Reitit’s is giving us space to see industry needs and hear the community’s voice.
See you at the next challenge!