In the recipes documentation, we aim to describe some patterns to organize your API calls to maximize performance.
If you can give the equivalent example in your own language-of-preference, please submit a pull request!
Python (Django)
Using Django with zgw-consumers
import base64
from datetime import date
from zgw_consumers.constants import APITypes
from zgw_consumers.models import Service
zrc_client = Service.objects.filter(api_type=APITypes.zrc).get().build_client()
drc_client = Service.objects.filter(api_type=APITypes.drc).get().build_client()
# zaak creation
today = date.today().strftime("%Y-%m-%d")
zaak_body = {
"zaaktype": "https://test.openzaak.nl/catalogi/api/v1/zaaktypen/4acb5ab8-f189-4559-b18a-8a54553a74ff",
"bronorganisatie": "123456782",
"verantwoordelijkeOrganisatie": "123456782",
"registratiedatum": today,
"startdatum": today,
}
zaak: dict = zrc_client.create("zaak", zaak_body)
# document creation
with open("/tmp/some_file.txt", "rb") as some_file:
document_body = {
"bronorganisatie": "123456782",
"creatiedatum": today,
"titel": "Example document",
"auteur": "Open Zaak",
"inhoud": base64.b64encode(some_file.read()).decode("utf-8"),
"bestandsomvang": some_file.size,
"bestandsnaam": some_file.name,
"taal": "nld",
"informatieobjecttype": (
"https://test.openzaak.nl/catalogi/api/v1/"
"informatieobjecttypen/abb89dae-238e-4e6a-aacd-0ba9724350a9"
)
}
document: dict = drc_client.create("enkelvoudiginformatieobject", document_body)
# relate them
zio_body = {
"zaak": zaak["url"],
"informatieobject": document["url"],
}
zio: dict = zrc_client.create("zaakinformatieobject", zio_body)
Javascript
import jwt from 'jsonwebtoken';
const CLIENT_ID = 'example';
const SECRET = 'secret';
// helpers
const getJWT = () => {
return jwt.sign(
{client_id: CLIENT_ID},
SECRET,
{
algorithm: 'HS256',
issuer: CLIENT_ID,
}
);
};
const apiCall = (url, method='get', body) => {
const fetchBody = body ? JSON.stringify(body) : null;
const token = getJWT();
const response = await fetch(
url,
{
method: method,
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
body: _body
}
);
const responseData = await response.json();
return responseData;
};
const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = error => reject(error);
});
// zaak creation
const today = '2020-10-16';
const zaakBody = {
'zaaktype': 'https://test.openzaak.nl/catalogi/api/v1/zaaktypen/4acb5ab8-f189-4559-b18a-8a54553a74ff',
'bronorganisatie': '123456782',
'verantwoordelijkeOrganisatie': '123456782',
'registratiedatum': today,
'startdatum': today,
}
const zaak = await apiCall(
'https://test.openzaak.nl/zaken/api/v1/zaken',
'POST',
zaakBody
);
// document creation
const someFile = document.querySelector('#myfile').files[0];
const documentBody = {
'bronorganisatie': '123456782',
'creatiedatum': today,
'titel': 'Example document',
'auteur': 'Open Zaak',
'inhoud': toBase64(someFile),
'bestandsomvang': someFile.size,
'bestandsnaam': someFile.name,
'taal': 'nld',
'informatieobjecttype': `https://test.openzaak.nl/catalogi/api/v1/
informatieobjecttypen/abb89dae-238e-4e6a-aacd-0ba9724350a9`
};
const doc = await apiCall(
'https://test.openzaak.nl/documenten/api/v1/enkelvoudiginformatieobjecten',
'POST',
documentBody
);
// relate them
const zioBody = {
'zaak': zaak.url,
'informatieobject': doc.url
};
const zio = await apiCall(
'https://test.openzaak.nl/zaken/api/v1/zaakinformatieobjecten',
'POST',
zioBody,
);
Key problem here is that one Zaak has (probably) multiple documents related to it, and retrieving them sequentially is a performance hit that gets worse with the amount of documents.
The solution is to use some form of threading/concurrency offered by your language.
Python (Django)
Using Django with zgw-consumers, we can use the concurrent.fututures.ThreadPoolExecutor
, where each job will run in its own thread. This gets close to retrieving all the documents in parallel instead of sequentially, resulting in a constant-time determined by the slowest API call.
from typing import List
from zgw_consumers.constants import APITypes
from zgw_consumers.models import Service
from zgw_consumers.concurrent import parallel
zrc_client = Service.objects.filter(api_type=APITypes.zrc).get().build_client()
drc_client = Service.objects.filter(api_type=APITypes.drc).get().build_client()
zaak_url = "https://test.openzaak.nl/zaken/api/v1/zaken/b604ea56-f01c-432e-8d61-fd4ab02893dc"
zios: List[dict] = zrc_client.list("zaakinformatieobject", {"zaak": zaak_url})
document_urls = [zio["informatieobject"] for zio in zios]
with parallel() as executor:
_documents = executor.map(
lambda url: drc_client.retrieve("enkelvoudiginformatieobject", url=url),
document_urls
)
documents: List[dict] = list(_documents)
Javascript
Similarly to the Python case, we leverage the Javacsript async/await event loop. Once we've collected all the URLs of documents to retrieve, we create promises and by using Promise.all
, all API calls are being performed in parallel (at least for the network IO part).
import jwt from 'jsonwebtoken';
const CLIENT_ID = 'example';
const SECRET = 'secret';
// helpers
const getJWT = () => {
return jwt.sign(
{client_id: CLIENT_ID},
SECRET,
{
algorithm: 'HS256',
issuer: CLIENT_ID,
}
);
};
const get = (url) => {
const token = getJWT();
const response = await fetch(
url,
{
method: 'get',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
},
}
);
const responseData = await response.json();
return responseData;
};
const zaakUrl = 'https://test.openzaak.nl/zaken/api/v1/zaken/b604ea56-f01c-432e-8d61-fd4ab02893dc';
const zios = await get(`https://test.openzaak.nl/zaken/api/v1/zaakinformatieobjecten?zaak=${zaakUrl}`);
const promises = zios.map(zio => get(zio.informatieobject));
const documents = await Promise.all(promises);