ApiBlaze is a tool to explore API specifications: Search for a keyword, filter for objects, properties, or endpoints, and immediately see descriptions and code examples. ApiBlaze helps you to answer a specific question about an API lightning fast. You can try it here: apiblaze.admantium.com.
When the user selects an API, the API elements search page is shown. On this page, users can search and see the different elements of the API specification: Its objects, properties and endpoints. This blog post is the starting point for implementing this page. It explains the components, and details how search, selection and representation of API objects is implemented. Properties and endpoints will be explained in the next article.
This article originally appeared at my blog.
Components of the API Elements Search Page
Once the user selects the API he wants to explore, the page ApiElementsSearch
is rendered. This page consists of the following components:
- a search bar for objects, properties & endpoints
- a radio button group to filter the search bar
- a popup section that displays the search results
- a section that renders the description of the currently selected result
- a section that renders a code representation of the currently selected result
Let’s start to code these components one after another.
Search Bar
The search bar is similar to the API spec search bar: It accepts any keyword, calls the backend to search for the current input value, and will update the shared state with the search results.
import { Component } from 'spac'
import SearchApiElementsAction from '../actions/SearchApiElementsAction.js'
export default class ApiElementsSearchBarComponent extends Component {
render = () => {
return `
<input type="text" id="api-elements-search-query" value="${this.getState()
.apiElementsSearchQuery || 'pod'}" spellcheck="false">
`
}
handleKeyUp (e) {
switch (e.keyCode) {
case 40: // Arrow down
e.preventDefault()
const searchResultDom = document.querySelector('div.search-result')
searchResultDom && searchResultDom.focus({ preventScroll: false })
break
default:
this.updateState({ apiElementsSearchQuery: e.target.value })
break
}
}
triggerSearch (keyword) {
new SearchApiElementsAction().run(keyword, json =>
this.updateState({ apiElementsSearchResult: json })
)
}
}
Result Popup
The result popup is also similar to the one for searching API specifications: It shows a list of all search results, rendered from the shared state. And it also has the same UI interactions, which is navigating with the arrow keys.
The representation is of course different. When searching for an API, the following data structure is delivered by the backend:
[
{
name: 'Pod',
containingObject: 'io.k8s.api.core.v1.Pod',
type: 'object',
description:
'Pod is a `collection` of "containers" that can run on a host. This resource is created by clients and scheduled onto hosts.',
score: 5
}
//...
]
This structure is rendered by the following method:
_renderSearchResults() {
Object.entries(apiElementsSearchResult).forEach((value, key) => {
const [i, object] = value
const { name, containingObject, description } = object
var html = `
<div class="search-result" tabindex="${key}" ref="${name}">
<div class="parent-object">
${containingObject}
</div>
<div class="title">
<i>${icons.endpoint}</i>
${name}
</div>
<div class="description">${description}</div>`
html = html.concat(`</div>`)
this.addDom('#api-search-results', html)
})
}
Overall, the following layout will be shown to the user:
API Element Description
Whenever an item in the search result list is selected - by clicking on it, or by pressing enter when the item is selected - it will be shown in the description and in the code example.
The description is a simple component: It takes the values from the currently selected items, and renders a HTML representation.
_renderObject(details) {
const { name, containingObject, description } = details
return `
<h2>${name}</h2>
<div class="title">
<i>${icons['object']}</i>
<strong>${containingObject}</strong>
</div>
<div class="description">${this.format(description)}</div>
`
}
Rendering the Code Example
The final component renders a comprehensive code example representation. For an object, the internal data looks like this:
{
name: "Pod",
// ...
properties: {
apiVersion: {
_type: "string",
_description: "\"APIVersion\" 'defines' the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources"
},
kind: {
_type: "string",
_description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds"
}
}
}
Based on this structure, the example code representation is created. The implementation is about 100 lines of code - too tedious to walk through, so I will just outline the general steps:
- Convert the JSON data structure into a well-formatted string representation
- Sanitize the string representation, e.g. removing extra whitespace, converting quotes
- Render each key-value pair into a code line, adding the line number
- Format the value pair, e.g. render backticks as
<code>
representations
This results in the following layout:
Review: ApiBlaze Project Requirements
By designing and implementing the API elements search page with the search bar, popup, description and code example, we are progressing towards fulfilling all requirements. Let’s take a look at the current status:
Display API Elements
- ✅ DIS01 - Show an objects description
- ✅ DIS02 - When an object is selected: Show its entire data model
- ⏸ DIS03 - When a property is selected: Show in which objects it is used
- ⏸ DIS04 - When an endpoint is selected: Show its request and response object
As you see, two more requirements need to be completed. Properties will represent not a code example, but a list of objects in which they are referenced. And endpoints will be represented completely different: Per HTTP method, the description, the request data model, and the response data model are shown.
Conclusion
This article explained how to implement the API elements search page. I explained how the page is structured into different components: The search bar, search modifier and the results popup to the left, and the selected elements description and code representation to the left. Details about the component’s implementation, and about the processing of the internal API specification to HTML, were discussed. The next article extends the API elements search page with the representation of properties and endpoints.