Crafting effective Microservices in Python

TL;DR: API First approach with Connexion are powerful tools to create effective Microservices. The use of API First brings the benefit of creating APIs that fulfil the expectations of your clients. Besides that, using Connexion will help you develop APIs in Python in a smooth manner.

Google’s acquisition of Apigee emphasises how important API’s are in today’s architecture of applications. Using Microservices with well crafted API’s is crucial for maintaining a successful business, as it simplifies the development of a complex software solutions. Growing companies experience a natural raise of businesses’ complexity. This complexity comes from market changes and to cater to customers’ demands. Companies like Zalando, which are faced with such challenges, have chosen to adopt Microservices. The adoption of Microservices aims to make it easier to build and maintain their applications.

Microservices are a style of breaking up and organizing a complex software solution into smaller composable services. Those smaller services can be independently maintained and deployed. Each service is developed around a business capability that usually provides a REST API. Clearly stating what capability each service provides can have a huge impact on productivity of the teams working together to build and maintain their Microservices. Microservices developers are expected to provide to client-side developers, who build and maintain the web, mobile or other services, a description of how to use their REST API’s. A failure to describe an API in a clear manner and to maintain an up to date documentation leads to broken system designs and frustration of team members. The API First approach is being established as a solution for those problems.

API First approach

The API First approach has been recently added to the original 12 factors of app development methodology—a set of rules and guidelines for developing robust applications that a group of experienced developers came up with while building the Heroku platform. It is an approach that focuses on having API specifications as a first-class artifact of the development process and using them as a contract to be shared among software developers and teams. Building the specification of a service upfront facilitates discussions among possible consumers of the API, creation of mocks of the API, and generating documentation, even before you write the first line of code.

Google, IBM, Microsoft, and other companies have come together to create the Open API initiative (OAI), to support the definition and establishment of a vendor independent format for describing REST APIs known as Open API Specification, formerly named Swagger 2.0. There are other formats available for describing APIs that can be used with an API First approach, the most known being API Blueprint and RAML, yet the Open API format currently holds a larger community of users and supporters. From a specification written using Open API format the initial code for the implementation can be easily generated. There is support for many languages and frameworks: Ruby, Java, Node, C#, etc. For Python, Connexion is the best choice in API First development since it relies neither on code generation nor needs boilerplate code.

Connexion is an open source framework built on top of Flask that facilitates the development of Microservices in Python following the API First approach. It was created by Zalando to meet the in-house demand for such solution. Connexion is in active development, I am one of the primary maintainers of the project, along with Joao Santos and Henning Jacobs.

Building a simple service in Python

The first step to build an effective microservice in Python is to describe what are the resources that are going to be available in our API using the OpenAPI Specification. We will focus on describing what routes, parameters, payloads and which response codes our API produce. We are starting with a simple example of an endpoint that responds with the string “Hello API!”.

# `my_api.yaml` file contents swagger : ' 2.0' info : title : Hello API version : " 0.1" paths : /greeting : get : operationId : api.say_hello summary : Returns a greeting. responses : 200 : description : Successful response. schema : type : object properties : message : type : string description : Message greeting

In the code snippet above we specified that our API has an endpoint “/greeting” that accepts requests with method “GET” and returns a status code 200, which represents success. Notice that the business logic is not defined in our specification, this part is left to the implementation of our endpoint which will be done in Python. The “operationId” is what define which Python function should be executed when the API call is made to your endpoint.

# `api.py` file contents def say_hello (): return { "message" : "Hello API!" }

As you can see, Connexion API handlers are free of boilerplate code. Normal Python functions that return a simple data structure are used as handlers for the API calls. Data structures returned by the handlers may be validated against the API specification by Connexion (if specified so). This validation is disabled by default to grant flexibility during development. Now we can use Connexion to glue our code to the API specification and have a server running. The easiest way to run our API is using the Connexion CLI tool:

# run this command in the same directory where you saved the previous files. $ pip install connexion # installs connexion, run only once. $ connexion run my_api.yaml -v

Now we can go to the browser and access the address http://localhost:5000/greeting and we should be able to see the message:

{ "message" : "Hello API!" }

To make it more dynamic we can change our specification to add a HTTP parameter for the user’s name. For that our specification should look like this:

# `my_api.yaml` file contents swagger : ' 2.0' info : title : Hello API version : " 0.1" paths : /greeting : get : operationId : api.say_hello summary : Returns a greeting. parameters : - name : name in : query type : string responses : 200 : description : Successful response. schema : type : object properties : message : type : string description : Message greeting

Now Connexion will pass an optional parameter to our function. To handle that we also have to change our Python function.

# `api.py` file contents def say_hello ( name = None ): return { "message" : "Hello {}, from API!" . format ( name or "" )}

Our Python code is now matching the API specification. Now we can re-start our server, press Ctrl+C and run again the following command to see those changes in play.

# run this command in the same directory where you saved the previous files. $ connexion run my_api.yaml -v

Once again our server will be listening on http://localhost:5000/greeting. Additionally, it is now possible to pass an optional parameter “name”. Connexion includes an API console interface which is available at http://localhost:5000/ui. This console UI is enabled by default make it easy to call endpoints of our API. It also serves as documentation of our microservice.

The API Console Interface that is bunddled in Connexion.

Now we know the basics of how to create a Connexion application. For a more detailed example please check the Connexion example project available on Github. The official Connexion documentation is a complete resource of information it’s functionalities. This documentation should be used as reference while developing RESTful Microservices using Connexion.

Moving forward

Companies are starting to realise the benefits of the API First approach. Etsy has published a blog post describing how the API First approach is helping them tackle challenges with providing a consistent API. At Zalando, API First is at the core of their software development lifecycle with a process for peer review feedback and with guidelines for creating RESTful Microservices, which are available open source.

We can find many open source tools that support API First approach available for diverse languages. Connexion is the perfect framework for a clean implementation of API First approach in Python and is in active development. Connexion does not rely on code generation. This leaves you free to evolve your API specification without breaking the code you already have implemented.

If you have any issues with Connexion, please feel free to post your problem to our issue tracker. You are also very welcome to send a Pull Request. We already have many contributors and you can join by improving the documentation or the code. You can find me on Twitter at @rafaelcaricio.

Post by: Rafael Caricio