Python data model examples
Data models with dataclasses - type-safe requests and responses, serialized to JSON and OpenAPI automatically.
- Building a data model for your API services revolves around the usage of Python's built-in dataclasses.
- Dataclasses allow you to define your models in a declarative way that lets you integrate them with your IDE for type-completion or type checking.
- Furthermore, Zato extends dataclasses to support auto-serialization to and from Python dicts, JSON and OpenAPI definitions.
- Just like the services, models can be hot-deployed to Zato servers without any restart
- Models are reusable across services or systems - they are not specific to REST only
- Input can also be declared without a model, as a tuple of field names - declaring input and output shows both forms side by side
Typical usage
- Each request and response object is a dataclass subclassing a
Modelclass - In your code, you work only with high-level business objects such as
GetClientRequestandGetClientResponsebelow - Zato automatically serializes the objects from and to JSON, Python dicts or OpenAPI definitions
# -*- coding: utf-8 -*-
# stdlib
from dataclasses import dataclass
# Zato
from zato.server.service import Model, Service
# ###########################################################################
@dataclass(init=False)
class GetClientRequest(Model):
client_id: int
@dataclass(init=False)
class GetClientResponse(Model):
name: str
email: str
segment: str
# ###########################################################################
class GetClient(Service):
input = GetClientRequest
output = GetClientResponse
def handle(self):
# Enable type checking and type completion
request = self.request.input # type: GetClientRequest
# Log details of our request
self.logger.info('Processing client `%s`', request.client_id)
# Produce our response - in a full service this information
# will be obtained from one or more databases or systems.
response = GetClientResponse()
response.name = 'Jane Doe'
response.email = 'hello@example.com'
response.segment = 'RNZ'
# Return the response to our caller
self.response.payload = response
# ###########################################################################
Let's invoke it:
$ curl http://pubapi:<password>@localhost:17010/zato/api/invoke/client.get-client \
-d '{"client_id":123}' ; echo
{"name":"Jane Doe","email":"hello@example.com","segment":"RNZ"}
$
Working with lists
- In the example below, we return a list of objects
- Dataclasses can be nested in other dataclasses, forming arbitrary hierarchies of objects in a data model
# -*- coding: utf-8 -*-
# stdlib
from dataclasses import dataclass
# Zato
from zato.common.typing_ import list_
from zato.server.service import Model, Service
# ###########################################################################
@dataclass(init=False)
class Phone(Model):
imei: str
owner_id: int
owner_name: str
# ###########################################################################
@dataclass(init=False)
class GetPhoneListRequest(Model):
client_id: int
@dataclass(init=False)
class GetPhoneListResponse(Model):
phone_list: list_[Phone]
response_type: str
# ###########################################################################
class GetPhoneDetails(Service):
input = GetPhoneListRequest
output = GetPhoneListResponse
def handle(self):
# Enable type checking and type completion
request = self.request.input # type: GetPhoneListRequest
# Log details of our request
self.logger.info('Processing client `%s`', request.client_id)
# Build our response now - in a full service this information
# would be read from an exteran system or database.
# Our list of phones to return
phone_list = []
# Build the fist phone ..
phone1 = Phone()
phone1.imei = '123'
phone1.owner_id = 456
phone1.owner_name = 'John Doe'
# .. the second one ..
phone2 = Phone()
phone2.imei = '789'
phone2.owner_id = 999
phone2.owner_name = 'Jane Doe'
# .. populate the container for phones tha we return ..
phone_list.append(phone1)
phone_list.append(phone2)
# .. build the top-level response element ..
response = GetPhoneListResponse()
response.response_type = 'RZH'
response.phone_list = phone_list
# .. and return the response to our caller
self.response.payload = response
# ###########################################################################
We can invoke it now with:
$ curl http://pubapi:<password>@localhost:17010/zato/api/invoke/phone.get-phone-list \
-d '{"client_id":789}'
{"response_type":"RZH",
"phone_list": [
{"imei":"123","owner_id":456,"owner_name":"John Doe"},
{"imei":"789","owner_id":999,"owner_name":"Jane Doe"}]}
$