Open edX REST API Conventions

Open edX REST API Conventions

Background

This document captures the conventions to be used for all Open edX REST APIs. 

Useful reading:

High Level Requirements

  • Consumer-Perspective. Design your API from the perspective of the consumer, NOT the perspective of your underlying implementation.  For example:

    • If the underlying implementation requires accessing multiple models or multiple apps/projects, this does not need to be reflected in a public interface.  From the perspective of the consumer, it's simply one thing they are requesting.

    • Keep CRUD operations together within its corresponding resource.  Why have the client go to one resource to read it and then another resource to write it?

  • Simple.  Keep the top-leveI resources clear and simple - focusing on what the client is looking to consume.  You can hide complexity within the parameters.

  • Separation of concerns.  Do not require the consumer to know any implementation details.  For example, an API shouldn't require the client to know the inter-dependencies of fields.  Rather, all such business rules should be owned by the server.  This allows the client to be lightweight and easily maintainable in the future.

  • Explicit Support Levels. APIs should make it clear whether they are experimental or more permanent as a part of their documentation.

  • Discoverability. Support HATEOAS where possible.  This allows us to change our URLs without needing to worry about updating the mobile apps if the app discovers its URLs from a base URL.

Conventions

1. URL Naming

  • Must: Keep your base URL simple and intuitive.  

    • Suggestion: Follow this basic URL structure if you are concerned about collisions within your service.  By "collisions", we mean TBD.

      /api/{API_NAME}/{VERSION}/...

      If collisions are not a concern (for example, when exposing APIs from context-specific IDAs), then you can eliminate the use of API_NAME.

      /api/{VERSION}/...
  • Must: Keep the API name flat.  Use two base URLs per resource: collection/identifier, e.g.

    /api/user/v1/accounts/{USERNAME}
  • It is not necessary that there is a one-to-one correspondence between an API and the Django app that provides it.  In fact, which Django app implements an API is an implementation detail and need not be bound to a public interface.

  • Resource name

    • Keep verbs out of the base URL

    • Must: Use plural rather than singular nouns

    • Must: Use concrete (e.g., blogs, videos, news) rather than abstract (e.g., items, assets) names

    • Must: Use Python conventions: use_underscores instead of CamelCase

  • Verbs

    • When no resource is involved, be direct and use Verbs instead of Nouns (p 19).  But only when absolutely necessary as we try to use nouns as much as possible.

    • /convert?from=EUR&to=CNY&amount=100

    • Make it clear in your API documentation that these “non-resource” scenarios are different.

See list of example APIs at the end of this document.

2. Identifying Resources

  • Nice: Do not expose database IDs where possible (Must for external APIs, per dev ops)

  • Must: edX resource identifiers

    • Users should be referenced by username.

    • Courses runs should be referenced via course run keys (eg. course-v1:edX+BlendedX+1T2018)

    • Catalog courses should be referenced via a catalog UUID.

    • Course blocks should be referenced via usage keys (eg. block-v1:org+course+run+type@sequential+block@2aa6fc9d8278)

  • Explicit Filters

    • Model system resource URL schemes as if all resources are available to all users

    • Include all necessary filters in the URL such that any user could theoretically access the resource

    • Separate filtering and authorization – ie, do not return different resource representations via the same URL based on the requesting user 

      • Example 1:  Choose "/profiles/john_harvard" over "/profile"
      • Example 2:  Choose "/courses?username=john_harvard" over "/courses"
    • See relevant conversation here for more context.

    • Remember, it's entirely possible that "/profiles/john_harvard" or "/courses?username=john_harvard" could be requested by the "administrator" user

    • Several benefits exist from an explicit filtering approach:

      • Ensures resources/results are individually-addressable

      • Enables discovery/sharing of resources, among other potential uses

      • Resource filtering mechanisms can be modified without impacting authorization mechanisms

      • Intermediate network gear can cache resource representations to improve performance (for open/unprotected resources)

  • Composite Resource (with multiple dimensions)

    • If an endpoint represents a relationship among multiple dimensions, the dimensions can be specified in the following ways:

    • As filter parameters:

      • /api/enrollments/v1/enrollments/?user={username}&course={course_id}
    • As comma-delimited resource keys in the URL:

      • /api/enrollments/v1/enrollments/{username},{course_id}
    • As a UUIDs to uniquely identify the relationship:

      • /api/enrollments/v1/enrollments/{enrollment_id}