REST in Python

Accept REST calls, react to HTTP verbs and invoke REST APIs from Python services.

REST channels expose your services as HTTPS endpoints and outgoing REST connections let your services call other APIs. Both are defined once, in the Dashboard or in enmasse, and your code refers to them by name.

Accept a REST call

The parsed JSON request is self.request.payload and whatever you assign to self.response.payload is serialized to JSON for the caller:

from zato.server.service import Service

class GetCustomer(Service):

    input = 'customer_id'
    output = 'customer_id', 'name', 'status'

    def handle(self):

        customer_id = self.request.input.customer_id

        self.response.payload.customer_id = customer_id
        self.response.payload.name = 'Jane Doe'
        self.response.payload.status = 'active'

Invoke it:

curl localhost:17010/api/customers -d '{"customer_id":"123"}'

React to HTTP verbs

Implement handle_<VERB> methods and each verb gets its own code path - a verb with no method receives 405 Method Not Allowed:

from zato.server.service import Service

class Customers(Service):

    def handle_GET(self):
        self.response.payload = {'customers': ['123', '456']}

    def handle_POST(self):
        customer = self.request.payload
        self.logger.info('Creating customer: %s', customer['name'])
        self.response.status_code = 201

Call a REST API

Look up an outgoing connection by name and call it with the verb you need - the payload, query parameters and headers are plain dicts and the response's JSON is already parsed in response.data:

from zato.server.service import Service

class SetBillingInfo(Service):

    def handle(self):

        payload = {'billing': '395.7', 'currency': 'EUR'}
        params = {'cust_id': '39175', 'priority': 'normal'}
        headers = {'X-App-Name': 'Zato'}

        conn = self.rest['Billing API']
        response = conn.post(self.cid, payload, params, headers=headers)

        self.response.payload = response.data

The same connection object has .get, .put, .patch and .delete too, and response.status_code carries the HTTP status the API returned.

Path parameters

If the connection's URL path contains a pattern, e.g. /api/billing/{phone_no}, the matching key from params fills it in and the remaining keys go to the query string:

from zato.server.service import Service

class GetBillingInfo(Service):

    def handle(self):

        params = {'phone_no': '271637517', 'period': '2026-08'}

        conn = self.rest['Billing API']
        response = conn.get(self.cid, params=params)

        self.response.payload = response.data

Configuration

The enmasse YAML for the channel and the outgoing connection the examples above use:

channel_rest:
  - name: api.customers
    service: get-customer
    url_path: /api/customers
    security: api.customers.key

outgoing_rest:
  - name: Billing API
    host: https://billing.example.com
    url_path: /api/billing/{phone_no}
    data_format: json
    security: billing.bearer_token

See also

FeatureWhat it does
REST channelsEvery option of a channel, from URL matching to CORS
Calling REST APIsRetries, timeouts, errors and responses of outgoing calls
AuthenticationAPI keys, Basic Auth, OAuth and bearer tokens on both sides
Data modelsTyped request and response contracts for REST services

Learn more