Skip to content

Commit adf2e7b

Browse files
taimoor-ahmed-1Muhammad Faraz  Maqsood
authored andcommitted
docs: ADR for normalizing nested json apis (#38305)
1 parent ba8cf65 commit adf2e7b

1 file changed

Lines changed: 182 additions & 0 deletions

File tree

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
2+
Reduce Deeply Nested JSON via Minimal/Flattened Views
3+
=====================================================
4+
5+
:Status: Accepted
6+
:Date: 2026-04-08
7+
:Deciders: API Working Group
8+
9+
Context
10+
=======
11+
12+
Some APIs return deeply nested JSON payloads (course structures, block trees, progress views).
13+
This makes payloads hard to parse, increases response size, and slows clients and automated agents.
14+
15+
Decision
16+
========
17+
18+
1. Provide a "minimal" representation option for complex resources:
19+
20+
* Query param example: ``?view=minimal`` or ``?fields=...``.
21+
22+
2. Normalize/flatten overly nested structures where possible:
23+
24+
* Prefer references/IDs with follow-up endpoints over embedding entire trees by
25+
default. This is the recommended pattern for server-to-server integrations and
26+
automated clients (e.g., AI agents), where minimizing coupling to a specific
27+
nested shape matters more than minimizing request count.
28+
29+
**Exception — frontend / MFE clients:** when a client needs to render a complete
30+
view in a single page load and the nested data is *always* needed together, an
31+
embedded full representation is acceptable to avoid costly sequential requests.
32+
In these cases, use an explicit opt-in such as ``?view=full`` or
33+
``?fields=<explicit-list>`` so the heavy payload is never the default and clients
34+
can still request only what they need.
35+
36+
3. Document response shapes in OpenAPI, including minimal vs full variants.
37+
38+
.. note::
39+
Endpoints that expose a flat list of nodes (e.g. a search result set or an enrollment
40+
list) are also subject to :doc:`0032-standardize-pagination-usage`. For *tree-shaped*
41+
responses, see the `Interaction with Pagination (ADR 0032)`_ section below.
42+
43+
Relevance in edx-platform
44+
=========================
45+
46+
* **Deeply nested payloads**: The course blocks API (``lms/djangoapps/course_api/blocks/views.py``)
47+
returns a tree of blocks with ``root`` and ``blocks`` (dict keyed by usage ID), each block
48+
containing ``id``, ``type``, ``display_name``, ``children``, ``student_view_data``, etc.
49+
Full trees can be large and hard to parse.
50+
* **Existing flexibility**: Blocks API already supports ``requested_fields`` and
51+
``block_types_filter`` to reduce payload; a ``view=minimal`` or ``fields=...``
52+
convention would align with this ADR.
53+
* **Modulestore/OLX**: ``openedx/core/djangoapps/olx_rest_api/views.py`` returns
54+
nested block OLX; providing a minimal (e.g. IDs + types only) view would help
55+
clients that only need structure.
56+
57+
Interaction with Pagination (ADR 0032)
58+
======================================
59+
60+
Tree-shaped endpoints are **out of scope** for ADR 0032's flat-list ``DefaultPagination``
61+
requirement, because "page N of a tree" is not well-defined when the tree structure itself
62+
provides the navigation context. However, unbounded child lists within a tree still pose a
63+
performance risk. When a node may have a large number of direct children (e.g. a course
64+
chapter with hundreds of units, or a library with thousands of components), one of the
65+
following two patterns MUST be used:
66+
67+
**Pattern A — Depth cap + child-fetch URLs (recommended for tree fidelity)**
68+
69+
Return the full structural representation up to a fixed maximum depth (default ``depth=2``).
70+
For subtrees beyond that depth, return the child usage ID and a follow-up URL instead of
71+
embedding the full subtree:
72+
73+
.. code-block:: json
74+
75+
{
76+
"id": "block-v1:edX+Demo+2026+type@chapter+block@week1",
77+
"type": "chapter",
78+
"display_name": "Week 1",
79+
"children": [
80+
{
81+
"id": "block-v1:...",
82+
"type": "sequential",
83+
"display_name": "Lesson 1",
84+
"children_url": "/api/courses/v1/blocks/block-v1:.../?depth=1"
85+
}
86+
]
87+
}
88+
89+
This preserves tree semantics while bounding response size. The depth default and maximum
90+
MUST be documented and honoured via a ``?depth=N`` query parameter.
91+
92+
**Pattern B — Flat paginated child list (recommended when order/count matters more than
93+
structure)**
94+
95+
When clients primarily need to enumerate children (e.g. a library component picker or a
96+
block-type filter), expose a flat paginated sub-resource following ADR 0032:
97+
98+
.. code-block:: text
99+
100+
GET /api/courses/v1/blocks/<parent_id>/children/?page=1&page_size=50
101+
102+
This separates structural navigation (tree) from bulk enumeration (paginated flat list).
103+
104+
**Guidance for** ``?fields=...`` **with large child sets**
105+
106+
If a client requests a field that would expand a large child collection (e.g.
107+
``?fields=children.student_view_data``), the server MUST NOT silently return all children
108+
unbounded. Apply whichever pattern above is appropriate for the endpoint and document the
109+
behaviour in OpenAPI. Returning HTTP 400 with a descriptive message is preferable to
110+
silently truncating or returning an oversized payload.
111+
112+
The Course Blocks API (``/api/courses/v1/blocks/``) is the canonical reference case: it
113+
already supports ``?depth=N`` and ``requested_fields``. Any extension of that API MUST
114+
continue to honour depth caps and MUST NOT add unbounded field expansions.
115+
116+
Code example
117+
============
118+
119+
**Query params for minimal representation:**
120+
121+
New endpoints should use ``fields`` (explicit field selection) and/or ``view``
122+
(named preset) consistently. The existing Blocks API uses ``requested_fields``
123+
for backwards compatibility; new work should follow the ``fields`` convention.
124+
125+
.. code-block:: text
126+
127+
GET /api/courses/v1/blocks/<usage_id>/?depth=1&fields=id,type,display_name
128+
GET /api/course_structure/v1/?view=minimal
129+
130+
**Response shape (minimal vs full):**
131+
132+
.. code-block:: json
133+
134+
// minimal (?view=minimal or ?fields=id,type,display_name,children)
135+
{
136+
"root": "block-v1:...",
137+
"blocks": {
138+
"block-v1:...": { "id": "...", "type": "chapter", "display_name": "Week 1", "children": ["..."] }
139+
}
140+
}
141+
142+
// full: same structure but with student_view_data, completion, block_counts, etc.
143+
144+
**Prefer IDs + follow-up over embedding:**
145+
146+
.. code-block:: text
147+
148+
GET /api/courses/v1/blocks/ → returns block IDs and types
149+
GET /api/courses/v1/blocks/<id>/ → returns full block when needed
150+
151+
Consequences
152+
============
153+
154+
* Pros
155+
156+
* Improved performance and developer ergonomics.
157+
* Easier integration for external services and AI agents.
158+
159+
* Cons / Costs
160+
161+
* Must maintain multiple representations: each ``view`` preset or ``fields``
162+
variant requires a separate serializer code path, its own test coverage, and a
163+
documented schema entry in OpenAPI. All variants must be kept in sync whenever
164+
the underlying data model changes (new fields, renamed keys, deprecated sub-objects).
165+
Without versioning discipline this becomes a source of subtle divergence bugs
166+
and undocumented breaking changes.
167+
168+
Implementation Notes
169+
====================
170+
171+
* Start with endpoints called out in the standardization notes (course structure, contentstore index,
172+
xblock, progress).
173+
* Measure payload size reduction and client performance improvements.
174+
175+
References
176+
==========
177+
178+
* :doc:`0032-standardize-pagination-usage` — Pagination standardization ADR. Tree-shaped
179+
endpoints are explicitly out of scope for ADR 0032's flat-list pagination requirement;
180+
this ADR (0036) governs how deep-structure responses should bound child list sizes at
181+
those same endpoints.
182+
* `Open edX REST API Conventions — “Multiple Formats” <https://openedx.atlassian.net/wiki/spaces/AC/pages/18350757/Open+edX+REST+API+Conventions#10.-Multiple-Formats>`_

0 commit comments

Comments
 (0)