Invoking Zato Python microservices with OpenAPI

One of the exciting additions of the upcoming Zato 3.3 release is the ability to invoke services through OpenAPI endpoints without a need for creation of REST channels explicitly - read more for details.

Python code

Supposing we have a service such as below - note that it is uses SimpleIO and that each of its input/output attributes is documented in the docstring - we would like to invoke it from an external application while delegating to Zato as much effort involved in it as possible.

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class GetUserAccount(Service):
    """ Returns user account details based on user ID and type.
    """
    name = 'api.get.user.account'

    class SimpleIO:
        """
        * account_id - ID of the user account to return
        * user_type - Type of the user the account belongs to

        * user_name - Name of the user to whom account_id belongs
        * needs_review - A flag indicating whether the account needs to be reviewed
        """
        input_required = 'account_id', 'user_type'
        output_required = 'account_name', 'needs_review'

    def handle(self):

        # Local aliases
        account_id = self.request.input.account_id
        user_type  = self.request.input.user_type

        # In reality, additional work would be carried out here first ..
        self.logger.info('Returning data for `%s` (%s)', account_id, user_type )

        # .. produce the output now.
        self.response.payload.user_name = 'my.user'
        self.response.payload.needs_review = True

REST channels

We will not pursue this path in this article but, in other circumstances, we could just create a REST channel for the service. This is great and it works perfectly fine, always letting one adjust many options, such as caching or rate-limiting.

However, today, we will auto-create API definitions using OpenAPI.

Creating OpenAPI specifications

Documentation, including OpenAPI specifications, can be created from the command line, as below:

$ zato apispec /path/to/server \
--dir /my/output/directory     \
--include api.*                \
--tags public                  \
--verbose

What we will find in the output directory is a Sphinx project with human-readable documentation, which is nice in itself, along with formal specifications, including OpenAPI.

Inspecting the OpenAPI specification

Upon downloading the openapi.yaml file we can confirm that it is a formal specification of the APIs exposed - including request and response schemas as well as endpoint addresses.

Note that, because we are using SimpleIO, the specification includes description of parameters and the parameters have expected data types, e.g. account_id is an integer while needs_review is a boolean.

components:
  schemas:
    request_api_get_user_account:
      properties:
        account_id:
          description: ID of the user account to return
          format: int32
          type: integer
        user_type:
          description: Type of the user the account belongs to
          format: string
          type: string
      required:
      - account_id
      - user_type
      title: Request object for api.get.user.account
      type: object
    response_api_get_user_account:
      properties:
        needs_review:
          description: A flag indicating whether the account needs to be reviewed
          format: boolean
          type: boolean
        user_name:
          description: Name of the user to whom account_id belongs
          format: string
          type: string
      required:
      - user_name
      - needs_review
      title: Response object for api.get.user.account
      type: object
paths:
  /zato/api/invoke/api.get.user.account:
    post:
      consumes:
      - application/json
      operationId: post__zato_api_invoke_api_get_user_account
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/request_api_get_user_account'
        required: true
      responses:
        '200':
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/response_api_get_user_account'
info:
  title: API spec
  version: '1.0'
openapi: 3.0.2
servers:
- url: http://localhost:11223

Using OpenAPI

With the specification downloaded, we can start to invoke our service immediately - in this case, we will use Postman. Essentially, import a definition, choose the desired service, provide input data and click the "Send" button.

Afterwards, we can also confirm in Zato server logs that the request was processed correctly.

Naturally, Postman is just a sample client utilizing the OpenAPI specification - clients in other languages can be easily used as well, including Python, Java, .NET, JavaScript and more.

Even more protocols

The above suffices for external clients to invoke your services purely based on their Python definitions with endpoints, documentation and specifications auto-generated by Zato.

But there is more. Have you noticed that nothing in the service used is specific to REST, OpenAPI or HTTP for that matter?

That is one of the benefits of using Zato - such a service is completely reusable across a range of other protocols, which means that it can exposed as-is through other channels, e.g. IBM MQ, AMQP, SOAP, WebSockets, Publish/Subscribe topics, Scheduler, File transfer and many more.

Be sure to check [the Zato documentation]/en/docs/3.3/index.html for more details!