Integrating with API gateways

Azure, AWS or Kong in front of Zato - one gateway channel that invokes your services.

Zato is itself an API management platform - channels, security, rate limiting, quota tiers and the OpenAPI console are built in, so no separate gateway is required in front of it.

If your environment already standardizes on an API gateway - Azure API Management, AWS API Gateway, Kong or similar - the gateway can handle security checks and rate limiting while all the business logic stays in Zato. The gateway invokes a single Zato REST channel, passing your services all the business data and metadata, and no individual service is exposed to the gateway directly.

This also lets you keep the Zato installation on your own premises, or in a secure cloud location with restricted public access, with a public API gateway in the cloud in front of it - how cloud services reach an on-premise installation is covered in on-premise systems and cloud services.

Your services work the same whether invoked directly or through the gateway - nothing in their code changes for this configuration.

Create the gateway channel

To create the channel, go to Connections > Channels > REST in the Dashboard, click Create a new channel and fill in the form:

  1. Name: Channel for Azure API Management
  2. Service: helpers.service-gateway
  3. Data format: JSON
  4. Security: a Basic Auth definition for the gateway to use
  5. Gateway services: the services the gateway may invoke, one name per line
  6. Click OK
A gateway channel

When you select helpers.service-gateway, the URL path is set to /zato/gateway/{service} automatically - you can change it to any other. The Gateway services list is the allowlist - a request naming any other service is rejected. In the channel list, gateway channels display a GW badge.

Write services

Services behind a gateway are regular Python services, with the gateway's metadata available on top of the business data:

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

# stdlib
from dataclasses import dataclass

# Zato
from zato.server.service import Model, Service

@dataclass
class GetCustomerRequest(Model):
    customer_id: int

@dataclass
class GetCustomerResponse(Model):
    name: str
    email: str
    tier: str

class GetCustomer(Service):
    """ Returns customer details for gateway and direct callers alike.
    """
    name = 'customer.get'

    input = GetCustomerRequest
    output = GetCustomerResponse

    def handle(self) -> 'None':

        # The end user the gateway authenticated
        username = self.channel.security.username
        self.logger.info('API call from %s', username)

        # The business data from the request
        customer_id = self.request.input.customer_id

        # A custom header the gateway forwards - absent for direct callers
        tenant = self.request.http.headers.get('x-zato-tenant')

        customer = self.invoke('customer.get-details', customer_id, tenant=tenant)

        self.response.payload.name = customer.name
        self.response.payload.email = customer.email
        self.response.payload.tier = customer.tier

The service behaves identically when called directly or through the gateway:

  • self.request.input contains the parsed request
  • self.channel.security.username contains the authenticated user
  • self.response.payload returns the response

HTTP headers

All HTTP headers that the gateway forwards to Zato are available in self.request.http.headers, with names normalized to lowercase with dashes, e.g. x-zato-tenant:

def handle(self) -> 'None':
    tenant = self.request.http.headers.get('x-zato-tenant')
    request_id = self.request.http.headers.get('x-request-id')
    correlation_id = self.request.http.headers.get('x-correlation-id')

You can iterate over all headers:

def handle(self) -> 'None':
    for key, value in self.request.http.headers.items():
        self.logger.info('Header %s = %s', key, value)

Pass security information

The API gateway authenticates the API clients, so it forwards the authenticated username to Zato in the x-zato-username header:

x-zato-username: john.doe
  • In Azure API Management, use a set-header policy
  • In AWS API Gateway, use a mapping template
  • In Kong, use the request-transformer plugin

Each gateway has its own mechanism, with the same result - the username arrives in the x-zato-username header and your services read it from self.channel.security.username:

def handle(self) -> 'None':
    username = self.channel.security.username
    self.logger.info('Request from user: %s', username)

This is the username of the end user who invoked the gateway - separate from the Basic Auth credentials the gateway itself uses to authenticate with Zato on the channel.

Call from the gateway

The gateway invokes services with the target service name in the URL path:

POST /zato/gateway/customer.get HTTP/1.1
Host: zato-server:17010
Content-Type: application/json
x-zato-username: john.doe
x-zato-tenant: acme

{"customer_id": 123}

Response:

{"name": "Jane Smith", "email": "jane@example.com", "tier": "premium"}

Alternatively, the service name goes in a query parameter:

POST /zato/gateway/invoke?service=customer.get HTTP/1.1

Both forms behave the same - some gateways express the path form more naturally and others the query parameter form.

Call directly

The same service can still be invoked directly, through its own REST channel:

POST /api/customer/get HTTP/1.1
Host: zato-server:17010
Content-Type: application/json
Authorization: Basic am9obi5kb2U6c2VjcmV0

{"customer_id": 123}

The response is identical and the service code does not change.

Gateway channels in enmasse

For automated deployments, gateway channels can be defined in YAML and imported with enmasse, so the gateway configuration is version-controlled alongside your services:

channel_rest:
  - name: Gateway
    url_path: /zato/gateway/{service}
    service: helpers.service-gateway
    data_format: json
    gateway_service_list:
      - customer.get
      - customer.update
      - orders.create
      - orders.list

See also

PageWhat it covers
REST channelsThe channel type gateway channels are built on
AuthenticationThe Basic Auth definition the gateway authenticates with
Enmasse referenceEvery channel_rest key, gateway_service_list included

Learn more