What Is OpenAPI? Definition, Benefits And Specifications

Technical
20.06.2022
What is OpenAPI?

What is OpenAPI?

OpenAPI is obviously an open standard for describing the REST API protocol. Like any other communication protocol, REST API requires some kind of specification that would act as a source of truth both for the client and server that exposes the API. It’s not a big deal to invent your way and describe this in a free file format. However, on a large scale, this approach doesn’t work well. Any server developer wants to distribute a document that would be good enough to make sure any client wouldn’t ask any questions, wouldn’t have any doubts about “which method should I use”, and makes integration seamless.
Let’s go further. What if a document we’re talking about would act not only as documentation and be human-readable but also would work as an input for your favourite programming language? OpenAPI specification solves this problem.

How businesses may benefit from using the OpenAPI?

These days, almost any web application is supposed to integrate with other services and thereby help build complex systems solving various problems. Such integration can’t live without an exposed API, while documentation for this API makes the life of your integrators much easier. The integration time is a crucial factor to build products on top of your existing ones. Make your integration hard and lose a lot of opportunities.

How to craft OpenAPI

Now let’s talk about the practical side of the question. OpenAPI specification is simply a yaml/json file. So, I prefer to edit it in editors that make the process simpler and protect it from errors. There’s a list of Open Api tools that are available in the market both free of charge and as a non-free option so that you can choose whatever is right for you. I personally use APiCurio.

Well, I’d like to give you a practical example, so let’s craft a simple Open APi example that exposes CRUD API for managing users.

  1. Let’s create a User data model
description: A simple user data model
required:
	- name
	- surname
type: object
properties:
	name:
    	description: Simply the name of the user
    	type: string
	surname:
    	description: Simply the surname of the user
    	type: string

2. Create a User response

content:
application/json:
    schema:
    $ref: '#/components/schemas/User'
description: A response containing user data model

3. Create endpoints with HTTP methods

get:
	parameters:
    	-
        name: id
        schema:
        type: string
        in: path
        required: true
	responses:
    	'200':
        $ref: '#/components/responses/UserResponse'
    	'404':
        description: When there is no user with such id
	summary: Get user by id
delete:
	parameters:
    	-
        name: id
        in: path
        required: true
	responses:
    	'200': {}
    	'404':
        description: When there is no user with such id
	summary: Delete user by the specified id

How to publish OpenAPI

Now let’s publish our API and make our documentation available to the public. 

In ApiCurion, you can do it like this: 

How to publish OpenAPI
Picture to show how to publish the API. (Don’t be confused  by button “Publish”)

You’ll get a popup with a public link. Let’s follow it and this is what you’ll get.

How to publish OpenAPI

You have a shiny and beautiful webpage for free with all of your documentation available online. Now if everyone wants to integrate with our server, we can give a link and enjoy.

OpenApi Java code generation

Another practical question is how to generate the code for a Java project that uses Spring and make use of the OpenAPI documentation we’ve crafted in the previous step.

  1. A maven plugin
<plugin>
            	<groupId>org.openapitools</groupId>
            	<artifactId>openapi-generator-maven-plugin</artifactId>
            	<version>5.3.0</version>
            	<executions>
                	<execution>
                    	<id>odysseus</id>
                    	<goals>
                        	<goal>generate</goal>
                    	</goals>
                    	<configuration>
                        	<inputSpec>openapi/UserCrudOpenApi.yaml</inputSpec>
                        	<generatorName>spring</generatorName>
                        	<apiPackage>broscorp.net.controller</apiPackage>
                        	<modelPackage>broscorp.net.model</modelPackage>
                        	<configOptions>
                            	<delegatePattern>true</delegatePattern>
                        	</configOptions>
                    	</configuration>
                	</execution>
            	</executions>
        	</plugin>

2. A generated controller class

3. A generated data model as well

I’ve covered only a portion of the features this maven plugin offers, but it can help crack on with the topic. If you’re going to discover it deeper, then I suggest that you should read the official documentation.

How to generate Angular code with OpenAPI

To make this article even more practical, let’s pretend there’s a developer who would like to build a front end using our API and Angular.

  1. First we have to add an npm plugin to the dev dependencies of package.json  https://www.npmjs.com/package/ng-openapi-gen
  2. Now let’s configure it by creating a subfolder called “openapi” and a file inside called “openapi-config.json”
  3. For our user api the configuration file will look as following:
{
  "$schema": "../node_modules/ng-openapi-gen/ng-openapi-gen-schema.json",
  "input": "openapi/userOpenApi.yaml",
  "ignoreUnusedModels": true,
  "output": "src/app/api/userApi",
  "module": "UserApiModule"
}

4. Let’s add the generation step as an additional script in the package.json scripts section:

"scripts": {
 "generate-user-api": "ng-openapi-gen -c openapi/openapi-config.json"
}

5. Now let’s run it and see the data model being generated:

/**
* A simple user data model
*/
export interface User {

 /**
  * Simply the name of the user
  */
 name: string;

 /**
  * Simply the surname of the user
  */
 surname: string;
}

6. And an API client service class was generated as well:

import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { BaseService } from '../base-service';
import { ApiConfiguration } from '../api-configuration';
import { StrictHttpResponse } from '../strict-http-response';
import { RequestBuilder } from '../request-builder';
import { Observable } from 'rxjs';
import { map, filter } from 'rxjs/operators';

import { User } from '../models/user';

@Injectable({
 providedIn: 'root',
})
export class ApiService extends BaseService {
 constructor(
   config: ApiConfiguration,
   http: HttpClient
 ) {
   super(config, http);
 }

 /**
  * Path part for operation userIdGet
  */
 static readonly UserIdGetPath = '/user/{id}';

 /**
  * Get user by id.
  *
  *
  *
  * This method provides access to the full `HttpResponse`, allowing access to response headers.
  * To access only the response body, use `userIdGet()` instead.
  *
  * This method doesn't expect any request body.
  */
 userIdGet$Response(params: {
   id: string;
 }): Observable<StrictHttpResponse<User>> {

   const rb = new RequestBuilder(this.rootUrl, ApiService.UserIdGetPath, 'get');
   if (params) {
     rb.path('id', params.id, {});
   }

   return this.http.request(rb.build({
     responseType: 'json',
     accept: 'application/json'
   })).pipe(
     filter((r: any) => r instanceof HttpResponse),
     map((r: HttpResponse<any>) => {
       return r as StrictHttpResponse<User>;
     })
   );
 }

 /**
  * Get user by id.
  *
  *
  *
  * This method provides access to only to the response body.
  * To access the full response (for headers, for example), `userIdGet$Response()` instead.
  *
  * This method doesn't expect any request body.
  */
 userIdGet(params: {
   id: string;
 }): Observable<User> {

   return this.userIdGet$Response(params).pipe(
     map((r: StrictHttpResponse<User>) => r.body as User)
   );
 }

 /**
  * Path part for operation userIdDelete
  */
 static readonly UserIdDeletePath = '/user/{id}';

 /**
  * Delete user by the specified id.
  *
  *
  *
  * This method provides access to the full `HttpResponse`, allowing access to response headers.
  * To access only the response body, use `userIdDelete()` instead.
  *
  * This method doesn't expect any request body.
  */
 userIdDelete$Response(params: {
   id: any;
 }): Observable<StrictHttpResponse<void>> {

   const rb = new RequestBuilder(this.rootUrl, ApiService.UserIdDeletePath, 'delete');
   if (params) {
     rb.path('id', params.id, {});
   }

   return this.http.request(rb.build({
     responseType: 'text',
     accept: '*/*'
   })).pipe(
     filter((r: any) => r instanceof HttpResponse),
     map((r: HttpResponse<any>) => {
       return (r as HttpResponse<any>).clone({ body: undefined }) as StrictHttpResponse<void>;
     })
   );
 }

 /**
  * Delete user by the specified id.
  *
  *
  *
  * This method provides access to only to the response body.
  * To access the full response (for headers, for example), `userIdDelete$Response()` instead.
  *
  * This method doesn't expect any request body.
  */
 userIdDelete(params: {
   id: any;
 }): Observable<void> {

   return this.userIdDelete$Response(params).pipe(
     map((r: StrictHttpResponse<void>) => r.body as void)
   );
 }

}

Just in a few single steps, we’ve generated a server code in Java&Spring boot with all the controllers, generated a Typescript data model, and got Angular services, thus saving a lot of developer time and decreasing the risk of human error during integration.

How to fetch OpenAPI spec from a remote repository

All of that is fine. At Broscorp, we like automating everything, so every time I modified an API via API Curio, I had to manually put the file into the project repo and let it generate. And if I forget to update the file, then everything goes wrong. Is there a way to automatically fetch the latest version of our specification? Yes, there is!

The maven plugin allows downloading a specific version from the repo. 

  1. Let’s put our specification into a separate repo so that we can track the changes and tag versions in git.
  2. Our repo is private, so we have to be able to authenticate via the maven plugin. Let’s create credentials for it in Bitbucket. Full article is here: https://support.atlassian.com/bitbucket-cloud/docs/app-passwords/

The most important part is here:

Create an app password

1. Select your avatar (Your profile and settings) in the upper-right corner of the top navigation.
2. Select Personal settings from the Your profile and settings dropdown menu.
3. Select App passwords under Access management.
4. Select Create app password.
5. Give the app password a name related to the application that will use the password.
6. Select the specific access and permissions you want this application password to have.

Copy the generated password and either record or paste it into the application you want to give access. The password is only displayed this one time.

3. Let’s configure the maven plugin the right way.  Previously we had a hardcoded path to the file sitting somewhere in the filesystem. Now since the file is in the remote repository we have to modify “inputSpec” tag and add another tag called “auth”.

<inputSpec>https://api.bitbucket.org/2.0/repositories/{username}/{repository-name}i/src/{branch-name}/{filename}</inputSpec>
<auth>Authorization: Basic ${env.BITBUCKET_AUTH_HEADER}</auth>

Let’s have a deeper look at the url we have crafted:

  • https://api.bitbucket.org/2.0/repositories/ – unchanged part of the url.
  • {Username} – is a bitbucket username. A root path for all of your repositories.
  • {repository-name} – obviously a repository name.
  • {branch-name} – in our case it’sthe master but can be whatever you want.
  • {filename} – a name of the yaml file containing your openapi spec.

4. Now it’s time to look at what {env.BITBUCKET_AUTH_HEADER} is Since Bitbucket is using Basic Authorization the following schema is used:

Authorization: Basic base64(username:app_password)

5. Let’s make the hash out of your bitbucket username and app_password created on step 2. For this purpose, we can use an online tool like this: https://www.blitter.se/utils/basic-authentication-header-generator/

6. And the last step is to add the base64 header to the bitbucket repository variables to let our CI/CD works.

Voilà!

The workflow now looks like this: 

  • Edit the specification properly in the APICurio and publish it.
  • Commit the changes to the repository.
  • Run pipelines both for your front end and back end and immediately verify if the latest changes break any tests and if they do, then go and fix them.

This article aims to provide a way to use the advantages of OpenAPI specification that simplifies the life of the web application developer a lot. Let’s count the problems solved: 

  • API can be edited online and discussed with the team easily.
  • API can be published and distributed.
  • All dependant projects download the latest possible version of the spec file.

If you build a family of microservices, develop web applications, want to renovate the old monolith, expose an API for your start-up, then ask Broscorp how to streamline the development process and make it super smooth and sustainable.

Learn more about our web application development services.

Get your free project assessment

Leave us your email and we will contact you shortly


    No, thanks
    Get a project estimation now! Contact us to discuss your project and to get an estimation!
    [contact-form-7 404 "Not Found"]