Schedule a demo

GraphQL

Overview

Zato can connect to any GraphQL server through outgoing connections. This lets your services query and mutate data on systems that expose a GraphQL API, such as Microsoft 365, GitHub, Shopify, Hasura or any custom GraphQL backend.

There are two ways to work with GraphQL in Zato:

  • String queries - write queries and mutations as plain strings, pass them to .execute() and Zato handles the rest
  • DSL queries - use the gql library's DSL module to build queries programmatically with Python objects and auto-completion

Both approaches use the same underlying outgoing connection created in Dashboard. The connection handles transport, timeouts and authentication transparently.

To get started, create a GraphQL outgoing connection in Connections -> Outgoing -> GraphQL in Dashboard, then use self.out.graphql in your service to access the connection by name.

All examples on this page assume a GraphQL server that exposes an airport operations schema with types such as Flight, Runway and Gate. The outgoing connection is called "Airport GraphQL" in Dashboard.

Querying a server

The simplest approach is to pass a query string to .execute(). The string is automatically parsed before being sent to the server.

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

# Zato
from zato.server.service import Service

class GetActiveFlights(Service):

    def handle(self):

        # Get the connection by the name configured in Dashboard
        conn = self.out.graphql['Airport GraphQL']

        # Query all flights that are currently airborne
        query = """
            {
                flights(status: "airborne") {
                    id
                    flight_number
                    origin
                    destination
                    status
                }
            }
        """

        result = conn.execute(query)

        self.logger.info('Flights: %s', result)

.invoke() is available as an alias for .execute() - both methods do exactly the same thing.

Querying with variables

Pass a params dict to provide GraphQL variables to your queries.

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

# Zato
from zato.server.service import Service

class GetFlightById(Service):

    input = 'flight_id'

    def handle(self):
        conn = self.out.graphql['Airport GraphQL']

        query = """
            query GetFlight($flight_id: ID!) {
                flight(id: $flight_id) {
                    id
                    flight_number
                    origin
                    destination
                    departure_time
                    gate {
                        name
                        terminal
                    }
                }
            }
        """

        params = {'flight_id': self.request.input.flight_id}
        result = conn.execute(query, params=params)

        self.response.payload = result

Running mutations

Mutations work the same way as queries - use .execute() with a mutation string and optional variables.

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

# Zato
from zato.server.service import Service

class AssignFlightGate(Service):

    input = 'flight_id', 'gate_id'

    def handle(self):
        conn = self.out.graphql['Airport GraphQL']

        mutation = """
            mutation AssignGate($flight_id: ID!, $gate_id: ID!) {
                assignGate(flightId: $flight_id, gateId: $gate_id) {
                    id
                    flight_number
                    gate {
                        name
                        terminal
                    }
                }
            }
        """

        params = {
            'flight_id': self.request.input.flight_id,
            'gate_id': self.request.input.gate_id,
        }

        result = conn.execute(mutation, params=params)

        self.logger.info('Gate assigned: %s', result)

Using the DSL

For more complex scenarios, the gql library's DSL module lets you build queries programmatically using Python objects. This gives you auto-completion in your IDE, compile-time field validation and avoids string manipulation.

Use .session() to open a connection that fetches the server's schema, then build queries using the DSLSchema object.

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

# gql
from gql.dsl import DSLQuery, dsl_gql

# Zato
from zato.server.service import Service

class GetFlightsDSL(Service):

    def handle(self):
        conn = self.out.graphql['Airport GraphQL']

        with conn.session() as (session, schema):

            query = dsl_gql(
                DSLQuery(
                    schema.Query.flights.select(
                        schema.Flight.id,
                        schema.Flight.flight_number,
                        schema.Flight.origin,
                        schema.Flight.destination,
                    )
                )
            )

            result = session.execute(query)

        self.logger.info('Flights: %s', result)

The DSL approach is particularly useful when query structure depends on runtime conditions:

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

# gql
from gql.dsl import DSLQuery, dsl_gql

# Zato
from zato.server.service import Service

class GetFlightsConditional(Service):

    def handle(self):
        conn = self.out.graphql['Airport GraphQL']

        with conn.session() as (session, schema):

            # Start with core fields
            fields = [schema.Flight.id, schema.Flight.flight_number, schema.Flight.status]

            # Add optional fields based on the caller's request
            if self.request.input.include_gate:
                fielschema.append(schema.Flight.gate)

            if self.request.input.include_crew:
                fielschema.append(schema.Flight.crew)

            query = dsl_gql(
                DSLQuery(
                    schema.Query.flights.select(*fields)
                )
            )

            result = session.execute(query)

        self.logger.info('Flights: %s', result)

Pinging the server

Use .ping() to verify that the connection to the GraphQL server is working. This sends a schema introspection query and returns True if the server responschema.

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

# Zato
from zato.server.service import Service

class PingGraphQL(Service):

    def handle(self):
        conn = self.out.graphql['Airport GraphQL']

        is_alive = conn.ping()
        self.logger.info('Server alive: %s', is_alive)

Security

GraphQL connections are secured the same way as all other Zato outgoing connections - by attaching a security definition to the connection. The security definition is created separately in Security in Dashboard and then assigned to the GraphQL connection when you create or edit it.

The following security types are supported:

  • Basic Auth - sends a username and password as HTTP Basic Authentication headers with every request to the GraphQL server
  • API key - sends a token in a configurable HTTP header, commonly used with services like GitHub's GraphQL API where the header is Authorization: Bearer <token>
  • OAuth - obtains a Bearer token from an OAuth endpoint and attaches it to each request, commonly used with Microsoft 365 and other enterprise GraphQL APIs

No code changes are needed when security is configured - the connection handles authentication transparently. Your service code looks the same regardless of which security mechanism is used.

Security definitions can also be managed through enmasse for automated deployments.

Custom headers

If you need to send additional HTTP headers with every request to the GraphQL server, use the "Extra headers" field on the connection. It accepts a JSON object where each key is a header name and each value is the header value.

For example, to send a tenant identifier and a custom tracing header:

{"X-Tenant": "acme", "X-Request-Source": "zato"}

These headers are merged into every request made through the connection, whether using .execute() or .session().


Schedule a meaningful demo

Book a demo with an expert who will help you build meaningful systems that match your ambitions

"For me, Zato Source is the only technology partner to help with operational improvements."

- John Adams
Program Manager of Channel Enablement at Keysight