The result of these discussions can be found here: edX REST API Conventions

Using Existing Established Conventions

READ THIS FIRSTApigee RESTful Conventions

We'll be using this document as our basis. This page will outline any deviation from the document in our own conventions.

Use two base URLs per resource

RESTful API Naming Conventions

Make sure a developer subscribing to multiple edX APIs  does not have to re-learn a new vocabulary for each API by standardizing on our nouns and URL structure.

General Structure: /api/[application]/[version]/...

Example: /api/enrollment/v0/users/bob/courses/edX/Demo101/2014T1

Where:

Versioning

Versioning Stability

Common Variables

Please refer to this list, and add to it, as we define our API vocabulary.

 

Variable NamePurpose
usersThe instructor, staff, or student that this API call is being made for. Example: user in the Enrollment API will return the enrollments for that user. This should be used instead of "student"
coursesThe course ID relative to this API call. Example, Enrollment API will return the enrollment details for the specified course, or a single enrollment based on this course, and a user.

Conventions on GET, POST, and PATCH

Use the following basic guidelines when constructing your views to ensure consumers of our APIs can anticipate their behaviors.

Technologies

Common technologies will reduce complexity and encourage conventions. 

Collections: Pagination and Handling Large Results

All our APIs should support pagination on list-based views if they may return a large number of results. The Django REST Framework supports pagination if your views are tightly coupled with your models.  If your API takes on the separation of a views --> API --> Data Layer, you may not be able to use these features.  Leveraging the Paginator from Django, you can still return a paginated response.  Here is an example decorator for doing so.

Pagination

Handling Large Results

Some APIs may return more results than are required for each consumer. Mobile, for example, may only want to get a subset of the results from an API to reduce the cost of each request. One option is to implement parameter filtering on the API, where only the specified attributes are returned. This gets a little complicated when the API supports PUT requests, where a consumer of the API may only be working with the partial state of an object. 

A simpler approach is to structure your return values into compartmentalized data structures, providing refined URL paths to each smaller object in the result. This can reduce the cost of each request.

Example from Enrollment API:

This URL will get you a user's enrollment in a course, as well as all the enrollment information specific to the course itself:

/api/enrollment/v1/courses/edX/DemoX/Demo_Course

{
    "created": "2014-10-08T21:18:56Z", 
    "mode": "honor", 
    "is_active": true, 
    "course_details": {
        "course_id": "edX/DemoX/Demo_Course", 
        "enrollment_end": null, 
        "course_modes": [
            {
                "slug": "honor", 
                "name": "Honor Code Certificate", 
                "min_price": 0, 
                "suggested_prices": [], 
                "currency": "usd", 
                "expiration_datetime": null, 
                "description": null
            }
        ], 
        "enrollment_start": null, 
        "invite_only": false
    }, 
    "user": "steve"
}

Note that the course_details are a separate dict in the response. Allow a URL to refine down to just get that information:

/api/enrollment/v1/courses/edX/DemoX/Demo_Course/course_details/

{
    "course_id": "edX/DemoX/Demo_Course", 
    "enrollment_end": null, 
    "course_modes": [
        {
            "slug": "honor", 
            "name": "Honor Code Certificate", 
            "min_price": 0, 
            "suggested_prices": [], 
            "currency": "usd", 
            "expiration_datetime": null, 
            "description": null
        }
    ], 
    "enrollment_start": null, 
    "invite_only": false
}

Exception Handling

{
    "developer_message" : "Verbose, plain language description of the problem for the app developer with hints about how to fix it.",
    "user_message":"Pass this message on to the app user if needed.",
    "field_errors": {
        "foo": {
            "developer_message": "",
            "user_message": ""
        }
    }
}

Open Question: If you want to return additional data or information with the error, such as a URL, or a list of valid request parameters, how do you attach this to the returned exception?

Combined responses from multiple resources

Pagination

Documentation

Multiple Response Formats

Naming Conventions

 

Authentication

 

Authorization

 

Eventing and Analytics

 

Notes from Discussions

Small section capturing notes from API discussions and meetings.

Discussion w/ Dave O. Matt D. and Steve S., Dec 5 2014

These are notes captured while discussing how to make some conventions and expectations for APIs built for the edX platform going forward.

Arch Lunch discussion, Dec 11 2014

Other subjects we would like to discuss / be opinionated about

API Conventions Meeting, Dec 23 2014