# REST channels

Expose any service as a REST endpoint that clients call over HTTP.

A REST channel connects an incoming HTTP request to one of your services. When a client calls the channel's URL path, Zato invokes the service the channel names, passes the request's parameters to it and returns the service's response.

## Create a channel {#create-a-channel}

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

1. **Name:** Customer Orders
2. **URL path:** the path clients call, e.g. `/api/orders`
3. **Service:** the service that handles requests to this path
4. **Data format:** JSON
5. Click **OK**

[Video: New REST channel](https://zatosource-production.b-cdn.net/docs/gfx/rest/channel-create.webm?v=1791298885)

Dashboard menu: Connections > Channels > REST

The channel starts accepting requests once you click OK - configuration changes propagate to all servers automatically, with no restarts.

You can also pick a **Security** definition in the same form - a channel without one accepts unauthenticated requests. [Basic Auth](https://zato.io/docs/security/basic-auth.html), [API keys](https://zato.io/docs/security/api-keys.html) and [bearer tokens](https://zato.io/docs/security/bearer-tokens/index.html) are covered in [authentication](https://zato.io/docs/dev/rest/authentication.html).

## Use data models {#use-data-models}

Define the request and response as Python dataclasses and Zato parses incoming JSON into the request model and serializes the response model back to JSON. The service below creates an order from three input fields:

```python
# -*- coding: utf-8 -*-

# stdlib
from dataclasses import dataclass

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

@dataclass(init=False)
class CreateOrderRequest(Model):
    customer_id: str
    product_id: str
    quantity: int

@dataclass(init=False)
class CreateOrderResponse(Model):
    order_id: str
    status: str

class CreateOrder(Service):
    """ Creates an order for a customer and product.
    """
    name = 'demo.rest.create-order'

    input = CreateOrderRequest
    output = CreateOrderResponse

    def handle(self) -> 'None':

        # The parsed request, already a CreateOrderRequest instance ..
        request = self.request.input

        # .. another service creates the order ..
        order_id = self.invoke('orders.create',
            customer_id=request.customer_id,
            product_id=request.product_id,
            quantity=request.quantity
        )

        # .. and the response model serializes back to JSON.
        response = CreateOrderResponse()
        response.order_id = order_id
        response.status = 'created'

        self.response.payload = response
```

```bash
curl -X POST http://localhost:11223/api/orders \
  -d '{"customer_id": "CUST-001", "product_id": "PROD-123", "quantity": 2}'
```

Models give your IDE the field types to check before runtime, and the same definitions drive [OpenAPI generation](https://zato.io/docs/dev/rest/openapi.html) for your APIs.

Fields that the caller may omit are declared with `optional` from `zato.common.typing_`:

```python
from zato.common.typing_ import optional

@dataclass(init=False)
class CreateOrderRequest(Model):
    customer_id: str
    product_id: str
    quantity: int
    notes: optional[str] = None
```

## URL path parameters {#url-path-parameters}

Use curly braces to capture parts of the URL as parameters. For a URL path like `/api/customers/{customer_id}/orders/{order_id}`, Zato extracts both values and makes them available to your service through `self.request.http.params`:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class GetCustomerOrder(Service):
    name = 'demo.rest.get-customer-order'

    def handle(self) -> 'None':
        customer_id = self.request.http.params['customer_id']
        order_id = self.request.http.params['order_id']

        order = self.invoke('orders.get', customer_id=customer_id, order_id=order_id)
        self.response.payload = order
```

```bash
curl http://localhost:11223/api/customers/CUST-001/orders/ORD-123
```

`params` holds URL path parameters only - the query string has its own attribute, shown below.

## Query string parameters {#query-string-parameters}

Query string parameters are available through `self.request.http.GET`:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

# Defaults for query string parameters that the caller may omit
_default_status = 'all'
_default_limit = 100

class SearchOrders(Service):
    name = 'demo.rest.search-orders'

    def handle(self) -> 'None':
        status = self.request.http.GET.get('status', _default_status)

        # Query string values are always text
        if limit := self.request.http.GET.get('limit'):
            limit = int(limit)
        else:
            limit = _default_limit

        orders = self.invoke('orders.search', status=status, limit=limit)
        self.response.payload = orders
```

```bash
curl "http://localhost:11223/api/orders?status=pending&limit=10"
```

## Other request details {#other-request-details}

The same `self.request.http` object holds everything else about the incoming request:

```python
# The HTTP method, e.g. 'GET'
method = self.request.http.method

# The URL path as received, e.g. '/api/orders'
path = self.request.http.path

# All HTTP headers, with lower-case names, e.g. headers['x-api-key']
headers = self.request.http.headers

# The caller's User-Agent
user_agent = self.request.http.user_agent
```

## JSON request body {#json-request-body}

For POST and PUT requests with JSON bodies, Zato parses the JSON automatically. Access the data through `self.request.input`:

```python
# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class CreateCustomer(Service):
    name = 'demo.rest.create-customer'

    def handle(self) -> 'None':
        name = self.request.input['name']
        email = self.request.input['email']

        customer = self.invoke('customers.create', name=name, email=email)
        self.response.payload = customer
```

```bash
curl -X POST http://localhost:11223/api/customers \
  -d '{"name": "Alice Smith", "email": "alice@example.com"}'
```

## Restrict HTTP methods {#restrict-http-methods}

By default, a channel accepts every method from the server's `[http] methods_allowed` list, whose shipped default is GET, POST, DELETE, PUT, PATCH, HEAD and OPTIONS - a request with any other method receives 405 before routing. To restrict a channel further, enter one specific method in its configuration. The details are under [URL path matching](https://zato.io/docs/dev/rest/url-matching.html).

For more control, implement [verb-specific handlers](https://zato.io/docs/dev/rest/verbs.html) in your service - one service then handles GET, POST, PUT and DELETE differently on the same URL.

## Data format {#data-format}

Set the channel's data format to match what clients send:

- **JSON** - Zato parses incoming JSON and serializes responses automatically. This is the typical choice for REST APIs. See [request and response handling](https://zato.io/docs/dev/request-response/index.html) for details and the [message building examples](https://zato.io/docs/dev/examples/messages.html) for how responses are built through plain dot access.

- **Form data** - for HTML form submissions or [webhooks](https://zato.io/docs/dev/rest/webhooks.html) sending form-encoded data. Access fields through `self.request.http.POST`. For file uploads, see [file handling](https://zato.io/docs/dev/rest/file-handling.html).

## What a channel controls {#what-a-channel-controls}

Each channel has its own settings, independent of every other channel:

- **Security:** one [security definition](https://zato.io/docs/dev/rest/authentication.html) or a [security group](https://zato.io/docs/dev/rest/security-groups.html) with many credentials
- **Rate limiting:** per-channel [rate limiting](https://zato.io/docs/admin/guide/rate-limit.html) rules
- **CORS:** preflights from localhost are answered automatically and the service itself serves other origins - see [CORS in REST channels](https://zato.io/docs/dev/rest/cors.html)
- **Audit log:** each user-defined channel records the requests it receives and the responses it sends in its own [audit log](https://zato.io/docs/admin/audit-log/index.html) - internal channels, such as the built-in publish/subscribe ones, are skipped
- **Response caching:** a channel can serve repeated requests from its cache, without invoking the service at all - the settings open from the channel's own caching link in its Dashboard row, see [response caching](https://zato.io/docs/dev/api-management/response-caching.html)
- **API versioning:** each major version of an API becomes its own channel and the old one is marked deprecated, with a sunset date - see [API versioning](https://zato.io/docs/dev/rest/versioning.html)
- **Delivery queue:** with **Use queue** on in the channel's Delivery tab, each request is stored and acknowledged at once and the service runs from the queue, with retries and a dead-letter queue - see [accept now, process later](https://zato.io/docs/dev/rest/channels.html#accept-now-process-later)

## Accept now, process later {#accept-now-process-later}

Turn on **Use queue** in a channel's **Delivery** tab and the channel stores each request as it arrives, answers HTTP 200 with `{"is_ok": true, "cid": "..."}` and runs the service later, from the queue - the service receives the request exactly as it would have had it run at once, and a run that raises is retried under the channel's retry and dead-letter settings. The service can customize the acknowledgment by overriding `get_queue_response`, which runs as the request is accepted and reads the stored message's id from `self.request.queue.msg_id`.

The details, the settings and the enmasse key for a fixed acknowledgment are in [delivery retries and dead-letter queues](https://zato.io/docs/dev/delivery.html#the-queue-on-a-channel).

## See also {#see-also}

- [URL path matching](https://zato.io/docs/dev/rest/url-matching.html) - How a request finds its channel - paths, methods and priorities
- [Authentication](https://zato.io/docs/dev/rest/authentication.html) - Basic Auth, API keys and bearer tokens for channels
- [HTTP verbs](https://zato.io/docs/dev/rest/verbs.html) - One service responding differently to GET, POST, PUT, PATCH and DELETE
- [Enmasse reference](https://zato.io/docs/dev/rest/enmasse.html) - Every channel\_rest key for YAML-based deployments
- [Delivery retries and dead-letter queues](https://zato.io/docs/dev/delivery.html) - The queue a channel accepts requests into and the service's run from it

## Learn more {#learn-more}

- [REST integration overview](https://zato.io/integrations/rest/) - Start here to see how teams build REST integrations
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - Master architectural patterns for production-ready integrations
- [Modern REST API tutorial](https://zato.io/tutorials/rest-api/python.html) - Design, build and secure REST APIs with Python
