Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions docs/decisions/0036-normalize-deeply-nested-json-apis.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@

Reduce Deeply Nested JSON via Minimal/Flattened Views
=====================================================

:Status: Proposed
:Date: 2026-04-08
:Deciders: API Working Group

Context
=======

Some APIs return deeply nested JSON payloads (course structures, block trees, progress views).
This makes payloads hard to parse, increases response size, and slows clients and automated agents.

Decision
========

1. Provide a "minimal" representation option for complex resources:

* Query param example: ``?view=minimal`` or ``?fields=...``.

2. Normalize/flatten overly nested structures where possible:

* Prefer references/IDs with follow-up endpoints over embedding entire trees.

3. Document response shapes in OpenAPI, including minimal vs full variants.

Relevance in edx-platform
=========================

* **Deeply nested payloads**: The course blocks API (``lms/djangoapps/course_api/blocks/views.py``)
returns a tree of blocks with ``root`` and ``blocks`` (dict keyed by usage ID), each block
containing ``id``, ``type``, ``display_name``, ``children``, ``student_view_data``, etc.
Full trees can be large and hard to parse.
* **Existing flexibility**: Blocks API already supports ``requested_fields`` and
``block_types_filter`` to reduce payload; a ``view=minimal`` or ``fields=...``
convention would align with this ADR.
* **Modulestore/OLX**: ``openedx/core/djangoapps/olx_rest_api/views.py`` returns
nested block OLX; providing a minimal (e.g. IDs + types only) view would help
clients that only need structure.

Code example
============

**Query params for minimal representation:**

.. code-block:: text

GET /api/courses/v1/blocks/<usage_id>/?depth=1&requested_fields=id,type,display_name
GET /api/course_structure/v1/?view=minimal

**Response shape (minimal vs full):**

.. code-block:: json

// minimal (?view=minimal or ?fields=id,type,display_name,children)
{
"root": "block-v1:...",
"blocks": {
"block-v1:...": { "id": "...", "type": "chapter", "display_name": "Week 1", "children": ["..."] }
}
}

// full: same structure but with student_view_data, completion, block_counts, etc.

**Prefer IDs + follow-up over embedding:**

.. code-block:: text

GET /api/courses/v1/blocks/ → returns block IDs and types
GET /api/courses/v1/blocks/<id>/ → returns full block when needed

Consequences
============

* Pros

* Improved performance and developer ergonomics.
* Easier integration for external services and AI agents.

* Cons / Costs

* Must maintain multiple representations; requires careful schema/versioning discipline.

Implementation Notes
====================

* Start with endpoints called out in the standardization notes (course structure, contentstore index,
xblock, progress).
* Measure payload size reduction and client performance improvements.

References
==========

* “Hard-to-Parse Deeply Nested JSON” recommendation in the Open edX REST API standardization notes.
Loading