# gRPC in Python

Call any gRPC endpoint from Python services with typed Protocol Buffer messages.

Python services call systems that expose gRPC APIs through outgoing connections, with strongly-typed Protocol Buffer messages - the platform handles TLS, security and error mapping.

All four gRPC RPC types are supported:

- **Unary** - one request message in, one response message out
- **Server streaming** - one request in, a stream of responses out, yielded as they arrive
- **Client streaming** - a stream of requests in, one response out, any iterable or generator works
- **Bidirectional streaming** - streams in both directions

## Create a connection {#create-a-connection}

To create a connection, go to **Connections** > **Outgoing** > **gRPC** in the Dashboard, click **Create a new connection** and fill in the form:

1. **Name:** Billing
2. **Address:** the gRPC server's address, e.g. `billing.example.com:50051`
3. **Proto file path:** the filesystem path of the service's `.proto` file
4. Click **OK**

[Video: New gRPC connection](https://zatosource-production.b-cdn.net/docs/gfx/outgoing/grpc-create.webm?v=1790440811)

Dashboard menu: Connections > Outgoing > gRPC

Every service can use the connection through `self.grpc`, passing the connection's name. The object it returns exposes the methods of the underlying gRPC service - you call them directly, passing Protocol Buffer messages.

## Connection options {#connection-options}

All the fields of the form, in the order they appear:

| Field | What it does | Default |
| --- | --- | --- |
| Name | A unique name of the connection, used in `self.grpc['Name']` | --- |
| Active | Whether services can use the connection | On |
| TLS | Whether to connect over TLS, switch it off only for plaintext servers | On |
| Address | Host and port of the gRPC server, e.g. `billing.example.com:50051` | `localhost:50051` |
| Security | A Basic Auth, API key or OAuth definition whose credentials are sent as call metadata | No security |
| CA certs file | A PEM file with CA certificates to verify the server's certificate against | Well-known CAs |
| Proto file path | A `.proto` file the server generates the client code out of | --- |
| Stub module | Your own pre-generated `*_pb2_grpc` module, used instead of a `.proto` file, e.g. `acme.billing.v1.billing_pb2_grpc` | --- |
| Stub class | The stub class to use, e.g. `BillingStub`, needed only when the code has more than one service | Found automatically |
| Ping timeout | How many seconds to wait for the server when pinging | `10` |
| Max send size | The biggest message that can be sent, in bytes | `104857600` (100 MB) |
| Max receive size | The biggest message that can be received, in bytes | `104857600` (100 MB) |

Each connection needs either "Proto file path" or "Stub module" - see below for how to choose.

## Provide the Protocol Buffer code {#provide-the-protocol-buffer-code}

Every gRPC service is described by a .proto file and the connection needs the Python code generated out of it. There are two ways to provide that code and each connection uses one of them:

- **Let the server generate it** - set "Proto file path" on the connection to the filesystem path of your .proto file. The server runs the Protocol Buffer compiler itself, at connection create and edit time, and keeps the generated modules in its own work directory. If the .proto file imports other .proto files, keep them in the same directory and they will be compiled together.

- **Deploy your own modules** - generate the code yourself with `python -m grpc_tools.protoc` and [hot-deploy](https://zato.io/docs/dev/hot-deploy.html) the resulting `*_pb2.py` and `*_pb2_grpc.py` modules like any other Python code. On the connection, set "Stub module" to the name of the `*_pb2_grpc` module and, if the module has more than one service, "Stub class" to the stub class to use.

When the server generates the code, you can download it from Dashboard through the connection's "Stubs" link - the modules arrive as a zip file you can add to your IDE for code completion. The downloaded copy is for your editor only, at runtime the server always uses what it generated itself.

## Unary calls {#unary-calls}

The example below registers a debit mandate - a single request message is sent and a single response is returned. The connection is called "Mandates" in Dashboard.

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

# Zato
from zato.server.service import Service

# Generated Protocol Buffer messages
import debit_mandate_pb2

class RegisterMandate(Service):

    def handle(self):

        # Get the connection by the name configured in Dashboard
        client = self.grpc['Mandates']

        # Build the request message ..
        request = debit_mandate_pb2.MandateRegistrationRequest(
            debtor_name='John Doe',
            debtor_identity_number='123456789',
            bank_account_number='GB29NWBK60161331926819',
            collection_frequency=3,
        )

        # .. invoke the gRPC endpoint ..
        response = client.RegisterMandate(request)

        # .. and use the typed response.
        self.logger.info('Response: %s (%s)', response.response_code, response.response_description)
```

## Server streaming {#server-streaming}

When the remote method streams its responses, the call returns a generator - iterate over it and each message is yielded as it arrives from the server, without ever being buffered into a list first.

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

# Zato
from zato.server.service import Service

# Generated Protocol Buffer messages
import billing_pb2

class ListInvoices(Service):

    def handle(self):

        client = self.grpc['Billing']
        request = billing_pb2.InvoiceListRequest(max_items=100)

        # Each invoice is processed as soon as the server sends it
        for invoice in client.ListInvoices(request):
            self.logger.info('Invoice: %s -> %s', invoice.invoice_id, invoice.amount_cents)
```

## Client streaming {#client-streaming}

When the remote method consumes a stream of requests, pass any iterable - a generator keeps the requests from existing in memory all at once.

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

# Zato
from zato.server.service import Service

# Generated Protocol Buffer messages
import billing_pb2

class SubmitPayments(Service):

    def handle(self):

        client = self.grpc['Billing']

        def payments():
            for item in self.request.input.payments:
                yield billing_pb2.Payment(
                    invoice_id=item['invoice_id'],
                    amount_cents=item['amount_cents'],
                )

        # The server replies with a single summary once the stream ends
        summary = client.SubmitPayments(payments())

        self.logger.info('Accepted %s payments, %s cents in total', summary.payment_count, summary.total_cents)
```

## Bidirectional streaming {#bidirectional-streaming}

Both patterns combined - send a generator of requests and iterate over the responses.

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

# Zato
from zato.server.service import Service

# Generated Protocol Buffer messages
import billing_pb2

class ReconcilePayments(Service):

    def handle(self):

        client = self.grpc['Billing']

        def payments():
            for item in self.request.input.payments:
                yield billing_pb2.Payment(
                    invoice_id=item['invoice_id'],
                    amount_cents=item['amount_cents'],
                )

        for receipt in client.ReconcilePayments(payments()):
            self.logger.info('Receipt: %s settled=%s', receipt.invoice_id, receipt.is_settled)
```

## TLS {#tls}

Connections use TLS by default. If the remote end's certificate is not signed by a well-known authority, point **CA certs file** at a PEM file with the certificates to verify it against. To use a plaintext connection, switch off **TLS**.

## Security {#security}

A gRPC connection is secured by attaching a security definition to it, as with any other outgoing connection. Every invocation includes the credentials as gRPC call metadata.

The following security types are supported:

- **Basic Auth** - sends the username and password in the standard authorization metadata key
- **API key** - sends the key in the metadata header the definition names
- **OAuth** - obtains a [Bearer token](https://zato.io/docs/security/bearer-tokens/index.html) from an OAuth endpoint, refreshes it as needed, and attaches it to each call

The connection attaches the credentials to every call - your service code stays the same regardless of the security mechanism.

## Error handling {#error-handling}

When a gRPC call fails, the error's status code is mapped to the matching Zato exception, with the details the server sent as the message. If your service lets such an exception bubble up to a REST channel, the channel returns the corresponding HTTP status to its own caller - a NOT\_FOUND from the gRPC backend becomes a 404 in your REST API, not a generic 500.

| gRPC status code | Zato exception | HTTP status |
| --- | --- | --- |
| INVALID\_ARGUMENT | BadRequest | 400 |
| UNAUTHENTICATED | Unauthorized | 401 |
| PERMISSION\_DENIED | Forbidden | 403 |
| NOT\_FOUND | NotFound | 404 |
| ALREADY\_EXISTS, ABORTED | Conflict | 409 |
| RESOURCE\_EXHAUSTED | TooManyRequests | 429 |
| UNAVAILABLE, DEADLINE\_EXCEEDED | ServiceUnavailable | 503 |
| Anything else | InternalServerError | 500 |

You can also catch the exceptions yourself - they are all importable from `zato.common.exception`:

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

# Zato
from zato.common.exception import NotFound
from zato.server.service import Service

# Generated Protocol Buffer messages
import billing_pb2

class GetInvoice(Service):

    def handle(self):

        client = self.grpc['Billing']
        request = billing_pb2.InvoiceRequest(invoice_id='inv-123')

        try:
            invoice = client.GetInvoice(request)
        except NotFound:
            self.logger.info('No such invoice')
        else:
            self.logger.info('Invoice: %s', invoice.customer)
```

## Pings and test invocations {#pings-and-test-invocations}

Each connection has a ping button in the Dashboard - it waits until the underlying channel is ready, confirming that the remote end is reachable. The Dashboard can also invoke unary methods directly - pick the method, provide the request as JSON and the response is shown as JSON too, so you can check a connection before any service code exists.

## Define connections in YAML {#define-connections-in-yaml}

Instead of filling out the Dashboard form, you can declare connections in YAML and import them with [enmasse](https://zato.io/docs/admin/enmasse.html), the tool that automated deployments and version-controlled configuration rely on. Every field is listed in the [enmasse reference](https://zato.io/docs/admin/enmasse-reference.html).

```yaml
outgoing_grpc:

  - name: Billing
    address: billing.example.com:50051
    proto_path: /opt/zato/proto/billing.proto

  - name: Mandates
    address: mandates.example.com:50051
    stub_module: debit_mandate_pb2_grpc
    security: mandates.oauth
```

## See also {#see-also}

- [GraphQL](https://zato.io/docs/dev/examples/graphql.html) - Query GraphQL endpoints when the system exposes GraphQL instead
- [REST outgoing connections](https://zato.io/docs/dev/rest/outconns.html) - Call external REST APIs with pools, timeouts and retries
- [Hot deployment](https://zato.io/docs/dev/hot-deploy.html) - Deploy generated Protocol Buffer modules with no restarts
- [Enmasse](https://zato.io/docs/admin/enmasse.html) - Define connections and security in YAML for automated deployments

## 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
