# Wednesday, 28 November 2012

I've published an example of a self-documenting REST API to Github (source).

Why a self-documenting REST API?

A typical REST API sample is hosted at http://restdoc-sample.herokuapp.com/hello, using Heroku's embedded Tomcat Java example.

As a Developer, your initial questions may be:

  • "What does the 'hello' endpoint do?"
  • "Does the endpoint support GET, POST, PUT, DELETE?"
  • "What headers and message body payloads does this endpoint support?"

Fortunately, the RESTDoc project has already defined a specification for documenting REST APIs. The intent of RESTDocs is for each endpoint to support the OPTIONS method and return a structured RESTDoc for the particular API endpoint.

Getting back to the Heroku hosted sample, submitting a curl request for the 'hello' endpoint using the OPTIONS method returns the sample RESTDoc below.

curl -X OPTIONS http://restdoc-sample.herokuapp.com/hello

In addition to supporting the OPTIONS method, the github sample also supports appending '/describe' to any API endpoint to view RESTDocs in web browser. Example: http://restdoc-sample.herokuapp.com/hello/describe.

Standardizing on RESTDocs opens up a number of interesting possibilities. Since transitioning away from SOAP, the web development community has lost the ability to auto-generate object proxies for calling web services. Webhook and Enterprise Service Bus (ESB) platforms have lost the ability to auto-discover web service endpoints and their supported messages.

Self-describing REST APIs, using RESTDocs and the OPTIONS method, are a compelling solution for enabling the service oriented enterprise and integrating the cloud.

	{
	  "schemas" : {
	    "http://some.json/msg" : {
	      "type" : "inline",
	      "schema" : {
	        "type" : "object",
	        "properties" : {
	          "id" : {
	            "type" : "string"
	          },
	          "content" : {
	            "type" : "string"
	          }
	        }
	      }
	    }
	  },
	  // Headers can be described at the server level and/or per-method
	  "headers": {
	    "request": {
	      "Authorization": {
	        "description": "This server uses a custom authentication scheme. See http://myapi.com/docs/auth",
	        "required": true
	      }
	    },
	    "response": {
	      "X-RateLimit-Total": {
	        "description": "The number of API calls allowed per-hour"
	      },
	      "X-RateLimit-Remaining": {
	        "description": "Number of requests remaining until next refill"
	      },
	      "X-RateLimit-Reset": {
	        "description": "The time at which X-RateLimit-Remaining will be reset back to X-RateLimit-Total"
	      }
	    }
	  },
	  "resources": [
	    {
	      "id": "HelloMessage",
	      "description": "An example hello message",
	      "path": "/{locale}/{messageId}{?seasonal}", // representing query params with L3 URI templates
	      "params": { // URI parameters descriptions
	        "locale": {
	          "description": "A standard locale string, e.g. \"en_US.utf-8\"",
	          "validations": [ { "type": "match", "pattern": "[a-z]+(_[A-Z]+)?(\\\\.[a-z-]+)?" } ]
	        },
	        "messageId": {
	          "description": "A free-form message string",
	          "validations": [ { "type": "match", "pattern": "[a-z_]+" } ]
	        },
	        "seasonal": {
	          "description": "Whether the message is seasonal.",
	          "validations": [ { "type": "match", "pattern": "^(true|false|yes|no)$" } ]
	        }
	      },
	      "methods": {
	        "PUT": {
	          "description": "Update or create a message",
	          "statusCodes": { "201": "Created" },
	          "accepts": [   // Representations accepted by the method on this resource.
	            { "type": "text/plain" },
	            { "type": "application/json", "schema": "http://some.json/msg" }
	          ],
	          "headers": { // Request headers only, response headers are defined under 'response'
	            "X-User-Token": {
	              "description": "Used to identify the user creating the message"
	            }
	          },
	          "response": { // Response representations this resource/method provides
	            "types": [
	              { "type": "text/plain" },
	              { "type": "application/json", "schema": "http://some.json/msg" },
	            ],
	            "headers": {
	              "Location": {
	                "description": "The URL of the created message"
	              }
	            }
	          },
	          "examples": [
	            {
	              "path": "/en_US/greeting",
	              "body": "Hello, world!"
	            },
	            {
	              "path": "/en_US/greeting",
	              "headers": {"Content-Type": "application/json"},
	              "body": "{\"id\":\"greeting\",\"content\":\"Hello, world!\"}!"
	            }
	          ]
	        },
	        "GET": {
	          "description": "Retrieve a message",
	          "statusCodes": { 
	             "200": "Message retrieved successfully", 
	             "404": "Message not found"
	          }
	        }
	      }
	    },
	    {
	      // This resource has no human-readable documentation, but still provides some info on how to use it.
	      "id": "FallbackLocale",
	      "path": "/fallback/{locale}",
	      "params": {
	        "locale": { 
	          "validations": [ { "type": "match", "pattern": "[a-z]+(_[A-Z]+)?(\\\\.[a-z-]+)?" } ]
	        }
	      },
	      "methods": {
	        "GET": { "statusCodes": { "200": "OK" } },
	        "PUT": { "statusCodes": { "201": "Created" } }
	      }
	    }
	  ]
	}	
Sunday, 02 December 2012 15:17:33 (Pacific Standard Time, UTC-08:00)
Hi,

just for your information. We just released the final verision of RestDoc. It can be found on http://www.restdoc.org.

I'm happy that anybody uses our specification.

Regards,
Thorsten
Thorsten Hoeger
Sunday, 02 December 2012 23:00:40 (Pacific Standard Time, UTC-08:00)
Great news. Thanks Thorsten!
Mike
Comments are closed.