Projects like Swagger UI, Slate or Spectacle offer great documentation functionality, but focus on presenting a single API - but what if we want to provide documentation for multiple APIs cleanly through a single portal?
With the growing number of teams supporting public APIs, or groups needing to provide an ‘internal catalog’ to other teams, having one place for users to understand how various APIs work together is crucial. Well designed and maintained documentation helps increase adoption, enforce best practices in using a service , and is a great way to present new functionality as it comes online.
In this series, we will look to create a solution by passing OAS definitions into Swagger UI dynamically using React.js (Part 1), customizing this basic portal (Part 2), and finally deploying to external hosts (Part 3). Further parts of this series will focus on smaller pieces of functionality — such as enhancing documentation with ‘walk through’ style pages, or connecting to privately hosted definitions. SwaggerHub will act as our host for the API specifications although later the series will explore alternative options for loading them.
The steps are intended for those with some familiarity with React and OpenAPI, but they should be straight forward enough to follow for those new to the tooling as well.
A template version of the final portal can be found HERE to kick start this project, and a running example can be found hosted HERE.
What is OpenAPI, Swagger UI, or SwaggerHub?
The OpenAPI Specification (OAS) is a REST definition standard, maintained by the OpenAPI Initiative — part of the Linux Foundation. The specification recently saw the release of its 3.0 version, and the adoption and support has continued to grow across the industry. Standards like OAS help facilitate a ‘design-first’ or ‘definition-driven’ development strategy, letting stakeholders plan out an API and its functionality before digging into code. Upcoming changes and test requirements can be communicated earlier as a plan exists for everyone to work against.
Swagger UI lives under the Swagger tool set, a collection of open source projects that support working with OAS. It has long been used in parallel to definitions as a way to quickly provide interactive documentation through minimal set up. We will leverage the node package provided by Swagger UI in our single page application.
SwaggerHub is a platform solution built by SmartBear from the ground up to support OAS at scale. It plays nicely with the idea of having multiple different APIs managed by a single organization or team, and we can use its back end to load these in as needed to our documentation portal. By using a tool like SwaggerHub, we can update and define our ‘current’ specifications to be shown to API consumers, while also support ongoing design and development.
Now that we understand the tools we will be using, lets ensure we have the required dependencies to build the project. Later in the series we will look at deploying this to an external host, or running inside a container — but for now lets just get it running locally. The goal here is to do this with as little configuration as possible to leave our deployment options open, so we will only have a single requirement to start: NodeJS.
Verify that you have a node version later than 5.2.0 installed by running
node -v as we will be using npx to build out our initial starting point.
Instead of putting together an entire React development environment we will use the create-react-app project as a starting point. This gives us all our dependencies and base structure in a single command.
To get started, navigate to the parent folder that will house your project and run
npx create-react-app oas-doc-portal
NPX will take care of building out the base project, you can verify that everything went smoothly by moving into the new project folder and starting up the default app.
$ cd oas-doc-portal
$ npm start
If everything renders correctly in the browser, we can move on to connecting this to our definition repository.
This would also a good time to connect the project to the GIT repository system of your choice and make an initial push.
Rendering a Single Specification
Before doing anything adventurous like passing in definitions dynamically, lets make sure we can load and render a single definition from the remote resource. This will give us a solid starting point to work against, and make it easy to recognize when we inevitably break something. The tutorial will use the Swagger Petstore definition available here: https://petstore.swagger.io/v2/swagger.json
The magic all happens through the swagger-ui node package so lets stop the application using
ctrl+c and add it to our application:
npm install swagger-ui to install swagger-ui to the project:
- Add swagger-ui under the import statements at the top of the page.
To save re-configuring it later , we can add our constructor to the app and create a state for the OAS link. We will update this value on the fly with new OAS links, so its a perfect place to hold the reference. This article digs a little more into how React handles state.
We render our documentation by passing swagger-ui a definition link. As the link will soon be updating dynamically, we should make use of the React life cycle to keep everything up to date — in this case
componentDidMount will do the job (we will tweak this later as our requirements change).
- Below the constructor we defined earlier, add the following:
The Swagger UI component supports a number of different parameters depending on how we want it to render but for our initial use case:
- We are telling it to render our documentation inside of a
api-data element we will create next.
- A reference to the URL stored in our state object telling it where to load the definition from.
Go ahead and remove the place-holder render elements that create-react-app generated for us, and replace them with our newly defined div requirement:
Now Swagger UI will look for the
api-data element on the page and render our documentation inside of it. If we start the application, and reload the browser we should see that the previous placeholder page now has a basic representation of the Swagger Petstore!
It’s not pretty, but the data’s there — and we can now update our
definitionLink value manually to get new API data rendered. Essentially the next steps will look to do this as part of the application itself.
The app at this point should look like:
Updating The Definition Dynamically
The end goal of our portal is to show a list of APIs, click between each one and to see the documentation update in our main window. To do this, we will show the list in a sidebar and update the
definitionLink value as the user clicks through the list.
As a first step, we need to think about where we will be pulling our list of APIs from. For the sake of this tutorial we will use SwaggerHub as our repository system to serve up the various API definitions. If you don’t already have a SwaggerHub account you can sign up for a free here, create an organization and add a few definitions. Alternatively you can use the example organization provided in this tutorial.
Now we have a SwaggerHub organization and a couple of definitions hosted there, we can begin thinking about pulling them into our portal. This will all be driven by the SwaggerHub API, so as a starting point we will write a new function to handle our different API calls and avoid rewriting to much code.
- Add a new
swaggerhub function to our app:
This function will allow us to make calls out to SwaggerHub at a few different levels, and should be flexible enough to support new functionality as we build it out down the line. Next we want to look at getting the ‘list’ of APIs.
The SwaggerHub API allows requests about an organization, and the response contains an array of API names and links to their locations that will be perfect for our use case.
Lets put the
swaggerhub function to use and create a new
getOrganizationData call. This will take a single input parameter, our organization’s name, and set the response from SwaggerHub as a state-level list of APIs.
Now update the state object to expect the
definitionList. This would also be a good time to add another key/value pair that will be utilized later, and we should bind the two new functions to the application.
We also referenced an organization in the
getOrganizationData call, but haven't stored that information anywhere. Down the line we're going to want to add a sub-header, and a company logo too so lets create a config file to store this information.
- In the
/src directory, create a new file called
- Add the following object to the config file and fill it out with your organization's information:
- Next import the config file into
- And finally create a
componentWillMount function to handle mapping the data as the page loads:
We’ve built out the blocks to handle getting the API list back — now its time to start to think about how we want to render that information out to our consumers. The next few steps will build a new component,
Sidebar, as well as a new element call
apiLink that will get populated inside the sidebar.
/src create a new file —
Sidebar.js. The sidebar will be pretty straightforward to start, just a basic structure and some of the display name data we have stored in the config file:
- In the
render section of the main App, update to include the new component —
Sidebar. We want to pass down the data and functions it will need at this point too:
- Make sure to import
Sidebar into the application as well. We should also bring in the CSS file from the swagger-ui module to clean up our documentation view a bit. We will explore customizing this later.
- To clean things up in the browser a little bit, lets add some quick CSS to make it clear what and where we are making changes going forward. In the
App.css file, we can start to organize our page:
Great! We have a basic structure for our sidebar (a header and a main body that will hold the links) — but we’re missing a key piece, the links themselves. We are already passing the bulk of the data we need through the incoming
props so its just a matter of writing out some logic to handle that and update our state object.
- Create a new file
/src/APILink.js and use it to handle the data that’s coming down through the Sidebar:
- There are a couple things we are asking for here that aren’t yet getting passed down. First, lets create the
updateDefintionLink function in App.js, bind it in the constructor and pass it into the Sidebar:
- Now we need to connect the two so lets update
Sidebar.js to include the following:
What got added?
- To start we are validating if the organization data has been loaded, and if it hasn’t making that request using
- Once we have the API data, we iterate over it and feed it into the new element
APILink that will handle updating our
definitionLink component based on the user’s selection. We check to see if it is ‘published’ in SwaggerHub, and then the link gets pushed to an
- Finally, we have a new import from
Perfect. If we reload the page now, we should see that our sidebar is populated with the names of all our different APIs! Lets quickly update our CSS to clean them up:
Now we should see all our links shown on the left hand side, and are able to be clicked. Only one problem — the documentation component isn’t actually updating ! Luckily this is a quick fix. Right now, our
updateDefinitionLink is doing its job (if we use
console.log we could see the URL being passed correctly) — Swagger UI just doesn’t know that its updated because of the life cycle stage we have it in.
To fix this, simply update our earlier
componentDidMount function to
App.js and when we reload the page it should update with each new link selection!
At this point we have a functional (although pretty basic) API documentation portal! We can continue to add new API versions or definitions, change publish and unpublish on our back end — and our documentation is always going to be up to date.
In the next part of this series, we will look at how we can efficiently start to style the sidebar and Swagger UI elements to personalize the documentation, before deploying to a couple of different hosting sites in the final part.
Below is the final code that we should have at this point.
Thanks for reading! Looking for more API resources? Subscribe to the Swagger newsletter. Receive a monthly email with our best API articles, trainings, tutorials, and more. Subscribe