Table of Contents

REST APIs

We should create the REST APIs which can do the following :

  1. Allow access to content and courses, certifications and live events.
  2. Users can search the catalog and enroll to products.
  3. Users can play and track enrolled courses, certifications and live events.
  4. Retrieve the progress information.
  5. Some time in future, should also support creating, activating and deactivating users and SSO login.

The APIs should implement the following:

  1. Authentication & Authorization
  2. Throttle Limit
  3. Documentation & Examples to consume the APIs

General guidelines for APIs

1. APIs should be RESTful.

2. APIs should support both XML and JSON response.

3. The sensitive information like passwords etc. should be exchanged over SSL.

4. The error conditions should be differentiated from each other clearly so that specific handlers can be written by the clients.

5. We should try to use the existing controller end points to expose the APIs. But , if we have to support multiple versions of APIs an the same time, we need to introduce a new set of end points for each version. The version information can be passed as a parameter.

Ex - http://api.crossbow.com/api/v1/courses

This is a cleaner and efficient way to support multiple versions at the cost of code duplication to some extent.

Authentication & Authorization

We can follow one of the following two mechanisms to implement authentication and authorization for APIs.

1. OAuth

We can implement Oauth 2.0 to support authentication and authorization of the third party apps that access our APIs.

The implementation details can be found here - https://wiki.exphosted.com/doku.php/oauth_2.0_for_apis

2. Simple API Key based

This is a simple process where each site will have an api key and also each user will have an api key.

The keys are generated as soon as the site or the user is created. They will be usable after activating the site/user.

With every request that is being sent to the API, we need to include the api key. Based on the key, the user/site is found and the privileges are handled for them. The code that handles all this can be pulled out to a rails plugin.

Conclusion

We will follow OAuth 2.0 for authentication as it is the industry standard.

API Access Flow

1. The application which needs the access is registered at - http:<siteurl>/client_applications/new

A registration email is sent.

2. The site admin will approve the site. An email notification is sent to the application owner with the consumer key and secret.

3. The client aplication sends a request to the token url of the api (http://api.crossbow.com/api/authenticate/token) with consumer key, consumer scecret, email and password of the crossbow user. If both the client application and user are authenticated and active an access token is returned in the response. Otherwise an error code is returned.

We expire access tokens every 60 days.

4. The client application sends requests to the api using the access token for next 60 days. When an invalid access token is sent the apis are not accessible.

5. When the access token is expired after 60 days a special exception with error code is generated and client handles this by performing step 3 again.

Oauth Token request parameters and response - https://wiki.exphosted.com/doku.php/oauth_2.0_for_apis#oauth_20_spec_discussion

Sample API request and response - https://wiki.exphosted.com/doku.php/rest_apis#sample_api_request_and_response

Sample API Request and Response

We use rabl gem (0.3.0) to construct our api response - https://github.com/nesquena/rabl

The error response will be constructed using a customized serialize module.

Lets consider the products api sample which lists all the products of the site and also allows to search through them.

Request URL - https://api.crossbow.com/api/v1/products

Parameters - access_token, format, :page, :per_page, :price_min, :price_max, :tag_id, :category_id, :order_by, :order

Only access_token is mandatory and the remaining are optional and have default values.

The default out put format is xml.

XML Response -

<products>
  <product>
    <product-type>LiveEvent</product-type>
    <price>0.0</price>
    <description>test live event</description>
    <product-id>62</product-id>
    <thumbnail-src-url> http://api.crossbow.com/images/icons/live_event_thumb.png </thumbnail-src-url>
    <id>38</id>
   <categories/>
   <free>true</free>
   <title>Test Live Event</title>
   </product>
   ..........
   ..........
<products>

JSON Response -

{"products":
  [{"product_type":"LiveEvent",
    "price":0.0,
    "description":"test live event",
    "product_id":62,
    "thumbnail_src_url":"http://api.crossbow.com/images/icons/live_event_thumb.png",
    "id":38,
    "categories":[],
    "free":true,
    "title":"Test Live Event"},
    {......},
    {......}
  ]
}

Error Codes

Any unsuccessful operation while accessing an api throws an exception which is handled and displayed along with the error code. The response is in the following format.

1. XML -

    <error>
      <description>access_token_expired</description>
      <error-code>ERR0007</error-code>
    </error>

2. JSON -

{"error":
     { "description":"access_token_expired","error_code":"ERR0007"}
} 

The following error codes are used as of now -

Error_code Description Comment
ERR0000 message description depends on the error occurred. Any un handled/generic Server error
ERR0001 Resource not found #{resource_name} The accessed resource/record is not found.
ERR0002 Resource not saved #{resource_name} When any record updation fails.
ERR0003 Resource not accessible #{resource_name} When current user has no privileges to access a resource.
ERR0004 message description depends on the error occurred. Custom error thrown by us.
ERR0005 message description depends on the error occured. Any oauth exception which is not specific to user. (ex - invalid client)
ERR0006 invalid_user When user authentication fails or user is inactive during the oauth token obtaining process.
ERR0007 access_token_expired When the current access token is expired.

Status Codes

We can display the following status codes in the response so that proper warnings can be shown on the client. This can be added some time in the future.

200 OK— Used for all the successful operations

400 Bad Request— Used as a general catchall for bad requests. For example, missing parameters like api key or a validation error on a model

401 Unauthorized— Used when user accesses a resource that is not allowed to be accessed by him.

404 Not Found— Used whenrequesting a resource that doesn’t exists. This includes the api end points that are not found.

405 Method Not Allowed—Used when a client attempts to perform an operation on a resource that it doesn’t support (for example, performing a DELETE on a resource that can't be deleted).

503 Service Unavailable— Used when client reaches the throttle limit.

Also, all the operations which fail due to an exception or error should include a valid error message in the response.

API Rate Limit (Throttle Limit)

We can throttle the API using a gem based on rack middle ware - https://github.com/datagraph/rack-throttle

Some of the instructions to use this gem inside our rails app can be found here - http://martinciu.com/2011/08/how-to-add-api-throttle-to-your-rails-app.html

We can specify the maximum number of requests per day or per hour as an option. This limit should only apply to our API calls.

Rate Limit - At this moment we can go with 1200 requests per hour per IP which means one 20 requests per minute. This number can be discussed and tweaked.

With every request to the API, a couple of headers will be appended in the response.

1. X-RateLimit-Limit - Shows the number of requests allowed per hour

2. X-RateLimit-Remaining - Shows the number of requests remaining in the current hour.

When the number of requests exceed the limit we can throw a 503 status code (Service Unavailable).

Rails 2 configuration - For rails 2 applications we need to config this gem in config/environments.rb if we need for all the environments or configure it only in the config/<environment>.rb where environment is the specific env you need this to work (Ex- staging).

The number of requests remaining at any time can be stored on a redis server or memcached.

We can store this information on redis server as we already use it for Chat application.

Note -

Redis seems to be ignoring the expiration if we update the value of any key. So, at this point of time there is no straight forward way to compute the count starting from the first request of the user.

So, every one hour of server, the count gets refreshed. It is not after one hour of client's first request.

Documentation & Examples

To be updated