Create a REST API model
In the previous tutorial, we reviewed how to create data models for property graph databases. By the end of this tutorial, you will master how to create a model for REST APIs using Swagger or OpenAPI specification.
You may also view this tutorial on YouTube. Summary slides can be found here.
While it is useful to be familiar with REST APIs in general, and the Swagger/OpenAPI specification in particular, the great advantage of Hackolade Studio's handling of REST APIs documentation is that the user does not have to know the syntax for Swagger or OpenAPI. You create your API with the user interface, and the application takes care of generating the proper syntax, thereby avoiding costly mistakes, and increasing productivity and API quality.
Hackolade takes a visual and schema-centric approach to building REST APIs, and fits well in API-first and design-first processes. It can also help understand existing APIs with a visual representation of Swagger or OpenAPI files, after reverse-engineering of such files, whether in JSON or YAML.
Given the rise of OpenAPI version 3, replacing the previous Swagger version 2 specification, the rest of this tutorial will focus on OpenAPI. Most of the principles below apply also to Swagger, even if some implementation features may vary.
For the purpose of this tutorial, we will build a very simple API for an URL shortener, i.e. a service letting:
- a user create an entry based on a full URL, and receive a short slug generated by the service;
- any system send a slug and get back the full URL.
The example is inspired by Shorty, an existing service, but the OpenAPI documentation is enhanced to demonstrate best practices and how to leverage the features of Hackolade Studio. By the end of the tutorial, you will have created a visual model for your API:
and generated the corresponding OpenAPI file:
Here is the link if you want to download this data model.
Design the API
An API is an interface for people or systems to consume a service. It is critical to put yourself in the shoes of the consumer, and make the API easy to understand and navigate the service. It must be usable, valuable, credible, and useful. Good design and good documentation go a long way towards adoption, along with collaboration and communication.
The sequence diagram for the first process to create an entry based on a full URL and receive a short slug generated by the service can be drawn as follows:
The sequence diagram for the second process to send a slug and get back the full URL is as follows:
Typically, Swagger tutorials teach you to build an API in the order of sections in an OpenAPI file: first the info section, then the paths, and finish with components. That would be like wanting to paint the house before the foundations are built... We prefer to start with what matters most, the core foundation of the API: the schema describing the structure of the service contract being exchanged between the producer and the consumer.
Accordingly, the process to create this API is as follows:
-
create reusable components
-
a schema for the exchange
-
a request body to submit a long URL, using the schema created in the previous step
-
a simple parameter to fetch the full URL from a previously generated slug
-
a couple of responses
- returning the generated slug (200 - OK) using the common schema
- returning an error message, reusable for different kinds of errors
-
an OAuth2 security scheme (could also be done at a later stage)
-
-
create resources with requests and responses
-
add endpoint, tags, and metadata information, plus color coding of responses
-
generate the OpenAPI documentation file, with API testing
Build the API
We will now use Hackolade Studio to build the API step by step. Let's create a new model and choose OpenAPI as a target. Remember that the application will automatically generate the OpenAPI file with proper syntax, as you build the API in the screens. You may check the progress at any time in the lower tab OpenAPI File.
Create reusable components
Often, multiple API operations have some common parameters or return the same response structure. To avoid code duplication and facilitate evolution, you can place the common definitions in the global components section and reference them using $ref. In Hackolade, components are handled like model references, and are maintained in the lower tab Components. If a component evolves, these changes are automatically reflected in all places where the component is referenced.
Schema components for the exchange
For this very simple service, we just need 2 schemas, one for the happy flow exchange, and one for error messages. Note that the url schema can be used both in the creation request and the 200 response. The url schema has just 2 fields: longURL and slug, both of string data type. And the error schema is also real simple with a status code according to the RFC 7231 standard, and a text message.
The steps to create a schema in Hackolade have been covered in an earlier tutorial in section "Hierarchical schema view".
If you already have complex schemas defined in other Hackolade target data models (or JSON Schema files, or a Hackolade Polyglot model, or in other OpenAPI files, or on SchemaHub), you may reference any of those as external references so they can be kept in sync with their master version for higher data quality and consistency.
Request body to submit a long URL
To create a request body in components, you do a right-click on the requestBodies box and get a contextual menu where you're guided to choose the appropriate option:
A template is created to guide you through the creation, with a standard media type application/json and its structure. You may create additional media types, from a list of popular ones, or by adding your own if needed:
In our case, we will rename the requestBody to urlBody and replace the schema object by a reference to the schema component created earlier.
You may wonder why the slug is also in the requestBody. That's because a feature of the service is to allow the end-user to specify a preferred custom slug, as well as a random system-assigned slug. To allow the former, we need to provide a field for it, along with the long URL. An added advantage is that the same schema can be used both for the request and the response.
We also want to give users examples to better understand how to use the API, for example with
- a summary: Choosing your own custom slug
- and a value: {"slug":"hck" ,"longUrl":"https://hackolade.com"}
It is possible to create multiple components requestBodies to pick from in your requests.
Parameter to fetch a full URL from a previously generated slug
Next, we follow similar steps steps to create the parameter that will be used in the second use case, when submitting a slug (in order to retrieve the full URL) Here, we choose a path. In other APIs, you could also choose a query, a header, or a cookie.
Again here, a template automatically creates the structure and you just need to enter the properties and make the adjustments that suit your use case:
A couple of responses
Next, we right-click on the responses component to create a response structure:
A standard template gets created:
We start first with a good response 200 - OK. We rename the response to 200 and give it a description, "Successful operation" for example. We also right-click on the schema box and choose the url schema referenced earlier:
As a result, the structure now references the component schema:
Then we follow similar steps to define a generic error message:
A security scheme
Finally, we create a security scheme for the OAusth2 access to the service:
with its corresponding parameters:
Create resources with requests and responses
We're done now with the creation of reusable components. It is time to assemble them in resource paths. This operation is done in the API Model lower tab.
Add a resource
We right-click in the canvas to get the contextual menu and create a resource:
and give the resource path a name:
Add a request
Then we right-click again the box to add a request:
The transaction is to submit a full URL and optionally a custom slug, so we choose a POST request operation
and add a description and a summary:
It is a good practice to assign a tag, which will allow grouping of requests in the documentation:
The easiest way to create the structure for a request is to work in the dedicated tab where a template is automatically created:
There are different ways to build a request, all represented in the template. In our case, we will replace the schema object by the reusable component created earlier by choosing Reference > Replace by Component > Component > urlBody in the contextual menu:
This operation creates a reference to the pre-defined requestBody urlBody:
We also create examples of a custom slug and a random slug, which will help the developer visualize the possible inputs.
Tip: If you ever need a requestBody or response to differ from its original definition, you may break the link with the definition by right clicking on the reference and choosing to replace by attributes. Then you may do your modifications, but you should be aware that the object will not be updated if the original component evolves.
Add a response
We right-click on the request and choose Add response from the contextual menu:
and choose the appropriate response code:
and give it a meaningful description:
Again here, it is easier to work in the dedicated tab to see the template and replace the schema with the reusable component created earlier, and which will be used to return information to the requester:
We also want to create a 400 response for requests that did not conform to the expected schema:
And a 403 response for forbidden requests that did not provide proper credentials:
The resulting API will visually look like this:
Now, we can build the resource for the second use case, including a request and 2 responses:
Note how easy it is to understand visually the schema contract of the exchanges. Note also the handy color-coding of the responses to make it easy to spot the response behavior: green for 200, red for 400, and yellow for 403.
We made sure to tag the request with the same urls tag, so both requests are now grouped in the Swagger documentation:
We're almost done with the creation of the API: we just need to add some finishing touches.
Finishing touches
It is now time to add the metadata, including the server endpoints, tag descriptions, and links to external documentation:
Generate the OpenAPI documentation file
By clicking on the lower tab OpenAPI file, we can now see the end result:
On the left, we can see the generated JSON (or YAML) syntax for the API we just created with the application. An on-board validator verifies the compliance with the OpenAPI specification. You may select the text, and paste it in your API process or in the Swagger editor to leverage server and client code generation for example. You may also export the file via Tools > Forward-Engineering > OpenAPI file. Or you can use the Command-Line Interface forEng command to integrate the generation of this file into a DevOps CI/CD pipeline.
On the right, you can now visualize the API interaction pane. If the service is connected, you may even give the API a try, and see the service in action.
Don't forget to name your model and save it. Here is the link if you want to download this data model.
There's a raging debate about code-first vs design-first when it comes to API creation. The above approach should demonstrate the advantages of the latter. Design-first will let you think through all the details, discuss alternatives with other stakeholders, evaluate impacts, etc. all without writing a single line of code. Some developers may disagree, but the obvious advantages are: lower Total Cost of Ownership, higher quality, easier evolution, and even quicker delivery of the project. While it may seem slower at the beginning, design-first clearly saves time overall because it reduces risks of rework.
In particular when multiple teams need to develop in parallel, it becomes indispensable to agree first on a contract for the API. Don't take our word for it: here's an article by Smartbear, the original creators of Swagger.
Next steps
The above API example was simple on purpose. In practice, APIs can be quite complex. And they inevitably evolve over time. We suggest the articles below to help think through a variety of addition considerations that go beyond the scope of this tutorial but are nevertheless important:
- various best practices and common pitfalls
Tip: when evolving an API, it is a nice touch to use this specification for change logs and create x-changlog extensions where applicable.
In this tutorial, we reviewed how to create a model for REST APIs using Swagger or OpenAPI specification. In the next tutorial, we will cover how to perform data modeling for polyglot storage and transmission, using our Polyglot Data Modeling capabilities.