# Declaring input and output

Declare what a service accepts and returns - parsed before handle runs, validated and documented in OpenAPI.

A service can declare what it accepts and what it returns, and there are two forms to do it in - a list of field names, or a [data model](https://zato.io/docs/dev/model/index.html). Either way, a declaration means the input is parsed before `handle` runs, required fields are enforced, optional ones read as `None` when not sent, and the declaration feeds the [OpenAPI documentation](https://zato.io/docs/dev/rest/openapi.html) generated for the service.

Declarations themselves are optional - without one, `self.request.input` holds the incoming message as it arrived, as the [requests and responses](https://zato.io/docs/dev/request-response/index.html) page shows.

## Names in a tuple {#names-in-a-tuple}

The simplest declaration is a tuple of field names. Each name is its own string in the tuple - one string is always one field, never a comma-separated list of them - and a single field needs no tuple at all:

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

# Zato
from zato.server.service import Service

class GetProfile(Service):

    input = 'customer_id', 'tier'
    output = 'display_name', 'is_active'

    def handle(self):

        customer_id = self.request.input.customer_id
        tier = self.request.input.tier

        self.response.payload.display_name = f'Customer {customer_id}, tier {tier}'
        self.response.payload.is_active = True
```

```python
class GetStatus(Service):

    input = 'customer_id'
```

## Optional fields - the dash prefix {#optional-fields-the-dash-prefix}

A leading dash makes a field optional. The minus sign is part of the declaration only, never of the name itself - `-priority` is read as `priority`:

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

# Zato
from zato.server.service import Service

class GetProfile(Service):

    input = 'customer_id', '-priority'

    def handle(self):

        customer_id = self.request.input.customer_id

        # None when the caller did not send it
        priority = self.request.input.priority

        if priority is None:
            self.logger.info(f'No priority given for {customer_id}')
        else:
            self.logger.info(f'{customer_id} has priority {priority}')
```

A field without a dash is required. A request missing one is rejected before `handle` runs, with an error naming the field, e.g. `Missing required input element: customer_id`. The dash is the whole mechanism - there is no separate `is_required` attribute.

## What a name says about its type {#what-a-name-says-about-its-type}

The name of a field determines the type it arrives as:

- Names starting with `by_`, `has_`, `is_`, `may_`, `needs_` or `should_` arrive as booleans
- Names ending in `_count` or `_timeout` arrive as integers
- Every other name arrives as a string

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

# Zato
from zato.server.service import Service

class UpdateSubscription(Service):

    input = 'customer_id', 'is_active', 'retry_count'

    def handle(self):

        # A bool, from the is_ prefix
        is_active = self.request.input.is_active

        # An int, from the _count suffix, ready for arithmetic
        retry_count = self.request.input.retry_count
        next_retry = retry_count + 1

        self.logger.info(f'Active: {is_active}, next retry: {next_retry}')
```

When a field needs a type these conventions do not cover - an integer that is not a count, a date, a nested structure - declare a [model](https://zato.io/docs/dev/input-output.html#data-models) instead. There is no `force_type` or other per-field override for names alone - a type the conventions cannot express is what models are for.

## Declaring output {#declaring-output}

Output takes the same form, and declaring it makes the response include the declared names only. Assigning a name that was not declared raises an error at the very line that assigns it, so typos never reach the wire:

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

# Zato
from zato.server.service import Service

class GetBalance(Service):

    output = 'account_id', 'balance'

    def handle(self):

        self.response.payload.account_id = 'A-1001'
        self.response.payload.balance = 250
```

A dict assigned as a whole is filtered to the declared names too, so an internal dict can be assigned without exposing undeclared fields. The full treatment of building responses - free-form payloads, list responses, nested structures - is on the [requests and responses](https://zato.io/docs/dev/request-response/index.html) page.

## Data models {#data-models}

The second form is a data model - a dataclass whose fields have real types, defaults and structure. `self.request.input` is then an instance of the model:

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

# stdlib
from dataclasses import dataclass

# Zato
from zato.common.typing_ import optional
from zato.server.service import Model, Service

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

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

class CreateOrder(Service):

    input = CreateOrderRequest
    output = CreateOrderResponse

    def handle(self):

        request = self.request.input # This is a CreateOrderRequest instance

        response = CreateOrderResponse()
        response.order_id = f'order-{request.customer_id}-{request.quantity}'
        response.is_confirmed = True

        self.response.payload = response
```

With a model, `quantity` above is an `int` because of its type annotation, and the naming rules do not apply. A request missing the field is rejected with a response that names it, which tells the caller exactly what to correct. Nested models, lists of models and everything else models can do is on the [data models page](https://zato.io/docs/dev/model/index.html).

## Which form to use {#which-form-to-use}

Names in a tuple are enough when the fields are strings, booleans and counts that the name conventions cover, which is most lookup and status services. A model is the right form when fields have types of their own, defaults, or structure - and once a request has more than a handful of fields, a model also gives the IDE completion and type checking that plain names cannot.

The two forms mix freely across services - one service can declare names while another in the same project declares models.

## Learn more {#learn-more}

- [Development documentation](https://zato.io/docs/dev/) - The complete reference for writing services
- [Requests and responses](https://zato.io/docs/dev/request-response/) - What a service receives, what it returns and how both are structured
- [Integration examples](https://zato.io/docs/dev/examples/) - Ready-made code for the systems you are likely to connect to
- [IDE and debugging](https://zato.io/docs/dev/ide/) - Write services in the Dashboard or in your own editor
- [Data models](https://zato.io/docs/dev/model/) - Declare inputs and outputs and have them validated for you
- [In-depth API tutorial](https://zato.io/tutorials/main/01.html) - The full platform tutorial, from installation to production patterns
