API First Approach with OpenAPI 3 and REST — Practical Software Development Part 3

Berk Gökden
Berk Gökden’s adventures
5 min readJun 2, 2020

--

Both Service-oriented architecture and microservices require well defined and documented APIs. Microservices is a subset of Service Oriented Architecture where we split our services into smaller isolated parts. The idea is smaller parts are easy to develop and faster to iterate. As I explained in my previous post, a microservice should be able to re-written in less than a sprint given the specifications.

The first problem is how to define a standard, well-documented API Specification that you can iterate over. Luckily there is already a commonly used standard: OpenAPI Specification formerly known as Swagger Specification. Swagger has years of experience in the API development field and with the support of Mulesoft, OpenAPI 3 has support and integration to RAML (RESTful API Modeling Language).

I recommend you to follow gRPC for internal services. For external services or services that require edge caching, the read-write separation you should use REST APIs.

OpenAPI Specification allows you to define, your API, Authentication methods, Access control definitions, documentation, and service details in one place and gives you tools to generate code and documentation.

In this post, we will build a small service starting with the API and generate its code, documentation, and build a quick prototype.

Let’s assume that our product manager wants a widget on our website that shows popular movies and let the user see some details about them. So you need to come up with an API discussing with Stakeholders which are product manager and frontend developers in this case.

We will build a service caches popular movies and short information about them using The Movie Database API. This is a very simple service that lists popular movies and also allows you to query individual movies. Sometimes, we add services to cache external API calls since they are limited, we don’t want to do external API calls frequently and we don’t want to share our API key publicly.

We had a meeting, negotiated with stakeholders, and come up with this API specification:

This is a very simple spec with 2 API calls and no authentication.
You can keep this API definition in a common repository or on the server-side as the source of truth. This specification allows you to generate both client and server-side code so it would be better to keep them in a common repository.

The second problem is how to implement this API as it is defined in the specification without too much hustle.

For this purpose, we will use openapi-generator developed by Swagger. You can install the way you prefer, I use brew for MacOS:

brew install openapi-generator

We will generate go server code from the API spec inside a project folder movielist which also includes standard files like Dockerfile, and README. Use the following command to generate the project:

openapi-generator generate -i https://gist.githubusercontent.com/bgokden/031400fc7d073c0762b7ed595e81eb68/raw/56097eb7fe844469a2e4066e2d4b93dd5c868a30/example_open_api_3_api_spec.yaml -g go-server -o movielist

As a developer, we don’t like to write documentation much, personally I like to keep documentation next to the code to keep it in sync so we will generate markdown documentation from the API spec in the project folder:

openapi-generator generate -i https://gist.githubusercontent.com/bgokden/031400fc7d073c0762b7ed595e81eb68/raw/56097eb7fe844469a2e4066e2d4b93dd5c868a30/example_open_api_3_api_spec.yaml -g markdown -o movielist

Check the project folder, you will have a project structure as follows:

A multistage Dockerfile is in the project root.
A Readme is available with basic documentation in the project root.
Most of the source code is under the ‘go’ folder.
Markdown documentation is under the ‘Apis’ folder.
A copy of the OpenApi Specification is under the ‘api’ folder

There can be some small problems in the generated code like I had an unused import. After removing it, I can already run:

go run main.go

And you will have a service with the defined API exposed on port 8080.

All the logic we need to implement is inside ‘go/api_movie_service.go’

We are going to add another file called `moviedb.go` under ‘go’ folder and implement all the logic there.

The file ‘moviedb.go’ uses a loading cache to store the list of popular movies and movie information. Now bind this methods to the API:

Only two lines needed to be changed to call the logic.

Export your API KEY from The Movie Database or use the one embedded in the code. Normally you should not embed credentials in code.

And run:

go run main.go

Check your endpoint with curl.
List popular movies:

$ curl localhost:8080/v1/movies
[{"id":419704,"title":"Ad Astra"},{"id":338762,"title":"Bloodshot"},{"id":454626,"title":"Sonic the Hedgehog"},{"id":38700,"title":"Bad Boys for Life"},{"id":385103,"title":"Scoob!"},
{"id":475557,"title":"Joker"},{"id":576156,"title":"The Lovebirds"},{"id":495764,"title":"Birds of Prey (and the Fantabulous Emancipation of One Harley Quinn)"},{"id":530915,"title":"
1917"},{"id":920,"title":"Cars"},{"id":686245,"title":"Survive the Night"},{"id":496243,"title":"Parasite"},{"id":545609,"title":"Extraction"},{"id":330457,"title":"Frozen II"},{"id":
508439,"title":"Onward"},{"id":420817,"title":"Aladdin"},{"id":181812,"title":"Star Wars: The Rise of Skywalker"},{"id":443791,"title":"Underwater"},{"id":210577,"title":"Gone Girl"},
{"id":568160,"title":"Weathering with You"}]

Get one movie:

$ curl localhost:8080/v1/movie/420817
{"id":420817,"title":"Aladdin","release_date":"2019-05-22","original_language":"en","poster_path":"/3iYQTLGoy7QnjcUYRJy4YrAgGvp.jpg","overview":"A kindhearted street urchin named Alad
din embarks on a magical adventure after finding a lamp that releases a wisecracking genie while a power-hungry Grand Vizier vies for the same lamp that has the power to make their de
epest wishes come true.","vote_average":7.1}

There is a Dockerfile in the project but I will use my own ‘nicer’ Dockerfile for Golang services:

Copy-paste this over the existing Dockerfile and build your docker image with:

docker build -t movielist .

And run it locally with:

docker run -p 8080:8080 movielist

Finally, you can find all the project code mentioned here on GitHub:

When you check the Github Repo, you will realize the root Readme has documentation and references to the API and Model documentation. I did this for simplicity. You can put it under a separate docs folder while generation.

You can also generate client code in other languages so frontend developers can just import the generated code and start using the client.

In this post, I wanted to explain that code generation based on API should be automated and standardized. Developers sometimes spend too much time on these tasks which are pretty boring and straightforward. It is sad for me to see a developer team trying to set up just the API for weeks, adding documentation tasks that are actually never completed because it needs to be updated every sprint.

Follow me on Github and Linkedin:

--

--