Skip to content

docs: add ADR for standardizing REST API URL structure - #39003

Open
Abdul-Muqadim-Arbisoft wants to merge 1 commit into
openedx:masterfrom
edly-io:docs/ADR-standardize_rest_api_url_structure
Open

docs: add ADR for standardizing REST API URL structure#39003
Abdul-Muqadim-Arbisoft wants to merge 1 commit into
openedx:masterfrom
edly-io:docs/ADR-standardize_rest_api_url_structure

Conversation

@Abdul-Muqadim-Arbisoft

@Abdul-Muqadim-Arbisoft Abdul-Muqadim-Arbisoft commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

Open edX REST URLs follow no consistent pattern. The /api/ prefix, the position of the version, pluralisation, word separators, and trailing slashes all vary, in places between adjacent lines of a single URLconf. The FC-0118 ADRs (0025-0037) standardize what happens inside an endpoint but never its address.

Add docs/decisions/0038-standardize-rest-api-url-structure.rst as an accepted ADR defining twelve rules: a leading /api/ prefix, singular API names with plural collections, domain-based rather than app-based naming, lowercase snake_case segments, mounts that declare their own prefix, exact-match routes with a required trailing slash, version position and form, hierarchy capped at one level of nesting, opaque-key identifiers resolved by shared path converters, verb-free resource paths, snake_case Django URL names, and a single URL namespace shared by the LMS and Studio. Existing endpoints migrate under OEP-21 with the conforming path mounted alongside the legacy one.

This restates the still-applicable rules from the Open edX REST API Conventions wiki that OEP-49 defers to, settles what that page left as TBD, and adds a CI conformance check so the convention is enforced rather than remembered.

Open edX REST URLs follow no consistent pattern. The /api/ prefix, the position
of the version, pluralisation, word separators, and trailing slashes all vary,
in places between adjacent lines of a single URLconf. The FC-0118 ADRs
(0025-0037) standardize what happens inside an endpoint but never its address.

Add docs/decisions/0038-standardize-rest-api-url-structure.rst as an accepted
ADR defining twelve rules: a leading /api/ prefix, singular API names with
plural collections, domain-based rather than app-based naming, lowercase
snake_case segments, mounts that declare their own prefix, exact-match routes
with a required trailing slash, version position and form, hierarchy capped at
one level of nesting, opaque-key identifiers resolved by shared path
converters, verb-free resource paths, snake_case Django URL names, and a single
URL namespace shared by the LMS and Studio. Existing endpoints migrate under
OEP-21 with the conforming path mounted alongside the legacy one.

This restates the still-applicable rules from the Open edX REST API Conventions
wiki that OEP-49 defers to, settles what that page left as TBD, and adds a CI
conformance check so the convention is enforced rather than remembered.

Findings recorded in the ADR:

- /api/courses/ is mounted in both services on unrelated implementations, both
  at v1, which blocks the endpoint-by-endpoint combined headless LMS+CMS
  migration.
- /api/enrollment/v1/enrollment and /api/enrollment/v1/enrollments/ are
  different views, so pluralisation is load-bearing today.
- Deprecated Org/Course/Run course keys contain slashes, so nesting one
  mid-path requires a shared path converter; the platform has three, in two
  apps, none reusable.
- Django resolves re_path with re.search, so unanchored patterns in
  course_experience and learner_home match under arbitrary prefixes.
Abdul-Muqadim-Arbisoft added a commit to edly-io/openedx-proposals that referenced this pull request Aug 21, 2026
…nvention

Address review feedback on PR openedx#805:

- Link every convention to its source ADR and that ADR's code-example
  section; link the docs/decisions folder from the Abstract and
  Reference Implementation.
- Add Convention 14 (REST API URL structure) from ADR 0038.
- Rework Convention 3: reframe as consolidating RPC-style action
  endpoints, clarify it is not about HTTP verbs, add when-to-apply
  guidance and an illustrative example.
- Fix Convention 1 to require @extend_schema(request=..., responses=...)
  when request and response serializers differ.
- Remove the redundant inline error-response example in favour of the
  linked ADR.

Refs: openedx/openedx-platform#39003

@bradenmacdonald bradenmacdonald left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is excellent. Thanks!

than ``enable_certificate_generation`` (see
:doc:`0031-merge-similar-endpoints`). A genuine non-resource operation may use
a verb, but must be marked as such in its OpenAPI description.
11. **Django URL names are** ``snake_case``**, descriptive, and unique within their

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is some formatting issue on this line.

Image

Comment on lines +205 to +206
``r'(?P<course_key_string>[^/+]+(/|\+)[^/+]+(/|\+)[^/?]+)'``. Deprecated
``Org/Course/Run`` keys contain ``/``, which Django's built-in ``<str:...>``

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need to worry about deprecated org/course/run keys at all for these APIs? Support for "old mongo" has been mostly removed, and I don't think most APIs need to support it at all.

Comment on lines +215 to +216
their own ``urls.py`` — ``content_staging``, ``olx_rest_api``,
``content_libraries``, and ``instructor`` all do — so a conformance check must

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are all pretty much core apps at this point; we can also just make them always enabled instead of having them use the django app plugins feature. (If that would be helpful)

**Shared opaque-key converter**, registered once per service with
``register_converter(CourseKeyConverter, "course_key")``. The regex is
``COURSE_KEY_PATTERN`` with the named group stripped and the alternations made
non-capturing, because a converter regex is embedded into a larger pattern:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this be added to edx-drf-extensions ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants