Schedule a demo

HTTP verbs in REST services

REST APIs use HTTP verbs to express intent: GET retrieves data, POST creates resources, PUT replaces them, PATCH updates them partially, and DELETE removes them. Zato lets you handle each verb with a dedicated method, so one service can respond differently depending on how it's called.

Instead of checking the HTTP method inside handle(), you implement handle_GET, handle_POST, and so on. Zato routes requests to the right method automatically, and returns 405 Method Not Allowed if a client uses a verb you haven't implemented.

Basic example: GET and DELETE

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

# stdlib
from http import HTTPStatus

# Zato
from zato.server.service import Service

class CustomerResource(Service):
    name = 'customer.resource'

    def handle_GET(self):
        customer_id = self.request.http.params.get('customer_id')
        customer = self.invoke('customer.get-by-id', customer_id=customer_id)
        self.response.payload = customer

    def handle_DELETE(self):
        customer_id = self.request.http.params.get('customer_id')
        self.invoke('customer.delete', customer_id=customer_id)
        self.response.status_code = HTTPStatus.NO_CONTENT

Mount this on a channel at /api/customers/{customer_id} and clients can:

# Get a customer
curl http://localhost:11223/api/customers/CUST-001

# Delete a customer
curl -X DELETE http://localhost:11223/api/customers/CUST-001

# Try POST - returns 405 Method Not Allowed
curl -X POST http://localhost:11223/api/customers/CUST-001

Full CRUD resource

A typical REST resource supports all CRUD operations:

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

# stdlib
from http import HTTPStatus

# Zato
from zato.server.service import Service

class OrderResource(Service):
    name = 'order.resource'

    def handle_GET(self):
        """ Retrieves an order by ID.
        """
        order_id = self.request.http.params.get('order_id')
        order = self.invoke('order.get-by-id', order_id=order_id)

        if not order:
            self.response.status_code = HTTPStatus.NOT_FOUND
            self.response.payload = {'error': 'Order not found'}
            return

        self.response.payload = order

    def handle_POST(self):
        """ Creates a new order.
        """
        data = self.request.payload
        new_order = self.invoke('order.create', data=data)

        self.response.status_code = HTTPStatus.CREATED
        self.response.headers['Location'] = f'/api/orders/{new_order["id"]}'
        self.response.payload = new_order

    def handle_PUT(self):
        """ Replaces an entire order.
        """
        order_id = self.request.http.params.get('order_id')
        data = self.request.payload
        updated = self.invoke('order.replace', order_id=order_id, data=data)
        self.response.payload = updated

    def handle_PATCH(self):
        """ Partially updates an order.
        """
        order_id = self.request.http.params.get('order_id')
        changes = self.request.payload
        updated = self.invoke('order.update', order_id=order_id, changes=changes)
        self.response.payload = updated

    def handle_DELETE(self):
        """ Deletes an order.
        """
        order_id = self.request.http.params.get('order_id')
        self.invoke('order.delete', order_id=order_id)
        self.response.status_code = HTTPStatus.NO_CONTENT

Test all the verbs:

# Create
curl -X POST http://localhost:11223/api/orders \
  -d '{"customer_id": "CUST-001", "items": [{"sku": "ABC", "qty": 2}]}'

# Read
curl http://localhost:11223/api/orders/ORD-123

# Update (partial)
curl -X PATCH http://localhost:11223/api/orders/ORD-123 \
  -d '{"status": "shipped"}'

# Replace (full)
curl -X PUT http://localhost:11223/api/orders/ORD-123 \
  -d '{"customer_id": "CUST-001", "items": [{"sku": "XYZ", "qty": 5}], "status": "pending"}'

# Delete
curl -X DELETE http://localhost:11223/api/orders/ORD-123

Collection vs item endpoints

Often you need two endpoints: one for the collection (/api/orders) and one for individual items (/api/orders/{id}). You can use separate services or handle both in one:

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

# stdlib
from http import HTTPStatus

# Zato
from zato.server.service import Service

class EmployeeAPI(Service):
    name = 'employee.api'

    def handle_GET(self):
        employee_id = self.request.http.params.get('employee_id')

        if employee_id:
            # Single employee
            employee = self.invoke('employee.get', employee_id=employee_id)
            self.response.payload = employee
        else:
            # List all employees
            department = self.request.http.params.get('department')
            employees = self.invoke('employee.list', department=department)
            self.response.payload = {'employees': employees}

    def handle_POST(self):
        # POST only makes sense for collections, not individual items
        data = self.request.payload
        new_employee = self.invoke('employee.create', data=data)
        self.response.status_code = HTTPStatus.CREATED
        self.response.payload = new_employee

HEAD and OPTIONS

For completeness, you can also handle HEAD (metadata without body) and OPTIONS (supported methods):

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

# Zato
from zato.server.service import Service

class DocumentResource(Service):
    name = 'document.resource'

    def handle_GET(self):
        doc_id = self.request.http.params.get('doc_id')
        document = self.invoke('document.get', doc_id=doc_id)
        self.response.payload = document

    def handle_HEAD(self):
        # Return headers only, no body - useful for checking if resource exists
        doc_id = self.request.http.params.get('doc_id')
        exists = self.invoke('document.exists', doc_id=doc_id)
        if not exists:
            self.response.status_code = 404

    def handle_OPTIONS(self):
        # Tell clients what methods are available
        self.response.headers['Allow'] = 'GET, HEAD, OPTIONS, DELETE'

Notes

  • When you implement handle_VERB methods, Zato ignores the handle() method
  • Unsupported verbs automatically return HTTP 405 Method Not Allowed
  • All verb handlers have full access to Zato features: request data, invoking other services, outgoing connections, etc.

Learn more