-
Notifications
You must be signed in to change notification settings - Fork 9
Conform to JSON:API v1.1 #101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
edan-bainglass
wants to merge
33
commits into
aiidateam:master
Choose a base branch
from
edan-bainglass:json-api
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The implementation as in aiida-core.
ab46ef1 to
5dc29bc
Compare
f133405 to
4eee90c
Compare
12ff5ab to
0cad084
Compare
ceef3b6 to
bef27f6
Compare
bef27f6 to
4b53894
Compare
4b53894 to
98e4acd
Compare
7a53d49 to
0f48e15
Compare
0f48e15 to
6aab6f2
Compare
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Based on #109
Supersedes #18
This PR implements a JSON:API adapter layer.
JSON:API package
Benchmarking
Rough GET estimates (in ms) before and after introducing JSON:API transformation
/nodes?page_size=#Note that foreign entity inclusion is negligable here due to efficient caching (no further DB fetching + transformation if foreign entity already seen), and only having 3 users in the testing environment. It may add more time in cases where foreign entities are unique, e.g., nodes of a group (if and when this is implemented).
Implementation notes
JsonApiAdapterclass (no instantiation needed)/users/1)/computers/1/metadata), and/nodes)QueryBuilderendpointSince the
QueryBuildercan return entities, projections, or a combination, its results are collected as the attributes of a single JSON:API resource. The endpoint supportsflat=Truevia a query parameter. It also allows the user to request the full serialization of an entity (in contrast to the default minimal serialization).Provenance graph traversal
Links used to be returned as the actual incoming/outgoing nodes. However, these weren't exactly node objects, but rather augmented node objects, with
link_labelandlink_typefields. Instead, they are now returned as link resources (containing the link info), with URL links to their source/target nodes, which are proper node objects. The linkage facilitates traversal, as every link contains source/target links, and every source/target contains incoming/outgoing links. So in principal, one can move forwardor backward
through the provenance graph.
Before
{ "total": 8, "page": 1, "page_size": 10, "results": [ { "pk": 692, "uuid": "ba0d9e48-f8d7-44bf-9929-bdfa753be43a", "node_type": "process.workflow.workchain.WorkChainNode.", "process_type": "aiida.workflows:quantumespresso.pw.base", "ctime": "2025-09-14T16:32:16.940329Z", "mtime": "2025-09-14T16:33:18.201701Z", "label": "", "description": "", "user": 1, "link_label": "iteration_01", "link_type": "call_calc" }, ... ] }After
{ "jsonapi": { "version": "1.1" }, "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=incoming&page=1&page_size=10", "last": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=incoming&page=1&page_size=10" }, "meta": { "total": 8, "page": 1, "page_size": 10 }, "data": [ { "id": "9b2b343b-6840-449e-9c18-813b430b9948:ba0d9e48-f8d7-44bf-9929-bdfa753be43a", "type": "links", "attributes": { "link_label": "iteration_01", "link_type": "call_calc" }, "relationships": { "source": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948" }, "data": { "id": "9b2b343b-6840-449e-9c18-813b430b9948", "type": "nodes" } }, "target": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/ba0d9e48-f8d7-44bf-9929-bdfa753be43a" }, "data": { "id": "ba0d9e48-f8d7-44bf-9929-bdfa753be43a", "type": "nodes" } } } }, ... ] }Examples
Resource
Before
{ "pk": 695, "uuid": "9b2b343b-6840-449e-9c18-813b430b9948", "node_type": "process.calculation.calcjob.CalcJobNode.", "process_type": "aiida.calculations:quantumespresso.pw", "ctime": "2025-09-14T16:32:17.828434Z", "mtime": "2025-09-14T16:33:17.520797Z", "label": "", "description": "", "computer": 1, "user": 1 }After
{ "jsonapi": { "version": "1.1" }, "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948" }, "data": { "id": "9b2b343b-6840-449e-9c18-813b430b9948", "type": "nodes", "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948" }, "attributes": { "pk": 695, "node_type": "process.calculation.calcjob.CalcJobNode.", "process_type": "aiida.calculations:quantumespresso.pw", "ctime": "2025-09-14T16:32:17.828434Z", "mtime": "2025-09-14T16:33:17.520797Z", "label": "", "description": "" }, "relationships": { "collection": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes" } }, "user": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/user" }, "data": { "id": "1", "type": "users" } }, "computer": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/computer" }, "data": { "id": "1", "type": "computers" } }, "attributes": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/attributes" } }, "extras": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/extras" } }, "repository_metadata": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/repo/metadata" } }, "incoming": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=incoming" } }, "outgoing": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=outgoing" } } } } }With foreign relationships
{ "jsonapi": { "version": "1.1" }, "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948?include=users,computers" }, "data": { "id": "9b2b343b-6840-449e-9c18-813b430b9948", "type": "nodes", "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948" }, "attributes": { "pk": 695, "node_type": "process.calculation.calcjob.CalcJobNode.", "process_type": "aiida.calculations:quantumespresso.pw", "ctime": "2025-09-14T16:32:17.828434Z", "mtime": "2025-09-14T16:33:17.520797Z", "label": "", "description": "" }, "relationships": { "collection": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes" } }, "user": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/user" }, "data": { "id": "1", "type": "users" } }, "computer": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/computer" }, "data": { "id": "1", "type": "computers" } }, "attributes": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/attributes" } }, "extras": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/extras" } }, "repository_metadata": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/repo/metadata" } }, "incoming": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=incoming" } }, "outgoing": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/9b2b343b-6840-449e-9c18-813b430b9948/links?direction=outgoing" } } } }, "included": [ { "id": "1", "type": "users", "links": { "self": "http://127.0.0.1:8000/api/v0/users/1" }, "attributes": { "email": "aiida@localhost", "first_name": "", "last_name": "", "institution": "" }, "relationships": { "collection": { "links": { "related": "http://127.0.0.1:8000/api/v0/users" } } } }, { "id": "1", "type": "computers", "links": { "self": "http://127.0.0.1:8000/api/v0/computers/1" }, "attributes": { "uuid": "7ee86671-18f9-4690-bfa0-da43ddcc5ace", "label": "localhost", "description": "Localhost automatically created by `verdi presto`", "hostname": "localhost", "transport_type": "core.local", "scheduler_type": "core.direct" }, "relationships": { "collection": { "links": { "related": "http://127.0.0.1:8000/api/v0/computers" } }, "metadata": { "links": { "related": "http://127.0.0.1:8000/api/v0/computers/1/metadata" } } } } ] } }Collection
Before
{ "total": 755345, "page": 1, "page_size": 10, "results": [ { "pk": 1, "uuid": "008c4bef-8622-4f87-8c09-cc9b2aca1eab", "node_type": "data.pseudo.upf.UpfData.", "ctime": "2025-06-17T08:53:40.504476Z", "mtime": "2025-06-17T08:53:41.128779Z", "label": "", "description": "", "user": 1 }, ... ] }After
{ "jsonapi": { "version": "1.1" }, "links": { "self": "http://127.0.0.1:8000/api/v0/nodes?page=1&page_size=10", "last": "http://127.0.0.1:8000/api/v0/nodes?page=75535&page_size=10", "next": "http://127.0.0.1:8000/api/v0/nodes?page=2&page_size=10" }, "meta": { "total": 755345, "page": 1, "page_size": 10 }, "data": [ { "id": "008c4bef-8622-4f87-8c09-cc9b2aca1eab", "type": "nodes", "links": { "self": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab" }, "attributes": { "pk": 1, "node_type": "data.pseudo.upf.UpfData.", "ctime": "2025-06-17T08:53:40.504476Z", "mtime": "2025-06-17T08:53:41.128779Z", "label": "", "description": "" }, "relationships": { "collection": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes" } }, "user": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/user" }, "data": { "id": "1", "type": "users" } }, "attributes": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/attributes" } }, "extras": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/extras" } }, "repository_metadata": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/repo/metadata" } }, "incoming": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/links?direction=incoming" } }, "outgoing": { "links": { "related": "http://127.0.0.1:8000/api/v0/nodes/008c4bef-8622-4f87-8c09-cc9b2aca1eab/links?direction=outgoing" } } } }, ... ] }Errors
Before
{ "details": "No result was found" }After
{ "jsonapi": { "version": "1.1" }, "links": { "self": "http://127.0.0.1:8000/api/v0/users/4" }, "errors": [ { "status": "404", "title": "NotExistent", "detail": "No result was found" } ] }Future work
Better OpenAPI documentation of specific types
Significant work has gone into standardizing responses (including errors). The goal is to design the API in such a way as to yield a machine-actionable OpenAPI schema. However, some parts require additional work to bridge the gap between generic JSON:API responses and resource/error-specific shapes. For example, the response of
/users/1returns in the JSON:API document'sdatafield a general resource shape, with empty attributes and relationships objects. In practice, however, these are known a priori. Same with most if not all errors. In principle, it is possible to specify these by extending the model system further. This work is left for future discussion/implementation.Update
Some of this is now implemented. For now (and maybe permenantly), relationships and included related resources are not typed in the documentation.
JSON:API-ish query parameters
JSON:API provides standards for:
page[number]=2&page[size]=5,page[offset]=7sort=label,-pkfilter[node.pk][<]=42&filter[node.node_type][like]=%Job%fields[uuid,label,description,attributes.value]There's some flexibility afforded here - see for example this JSON:API implementation, as well as the JSON:API docs themselves.