Skip to content

Instantly share code, notes, and snippets.

@afair
Created May 13, 2014 19:13
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save afair/7a3fccb72f54ed8bf189 to your computer and use it in GitHub Desktop.
Save afair/7a3fccb72f54ed8bf189 to your computer and use it in GitHub Desktop.
REST, HTTP, JSON-API Protocol Notes

REST, HTTP, JSON-API Protocol Notes

Resources (URL):

Collection of Data Objects

  • /dataname (logical viewname (preferred), record name, table name)
  • /parent/id/children

Data Object

  • /dataname/id
  • /parent/id/children/id

Service endpoint

  • /servicename (e.g. validator)
  • /dataname/id/servicename

REST Actions to a Resource:

METHOD    COLLECTION       ELEMENT/DATA OBJECT     SERVICE
--------+----------------+-----------------------+------------
GET       Index            Show
POST      Create           Create sub-collection   Perform
PUT       Replace All      Replace
PATCH     Update Parts     Update some attributes
DELETE    Delete All       Delete
HEAD                       ETag, Caching checks

HTTP REQUEST

After opening a socket to the web server, the following is sent:

[method, "/resource?query", {headers}, optional-body]

A detailed request:

METHOD /path/resource?<query_string> HTTP/1.1
Host: example.com
Content-Type: application/vnd.example.api.v1+json, application/json
Content-Type: application/x-www-form-urlencoded or multipart/form-data
Accept-Charset: utf-8
Accept-Encoding: gzip, deflate
Accept-Language: en-US
Authorization: token <tokendata>
Cookie: name=value;
Content-Length: 123
From: user@example.com
Range: bytes=start-stop
User-Agent: <agent string>

<body>

QUERY_STRING: Selection or Meta Info

Typically used for query parameters: name=value&...

  • Query: q=searchterms
  • Form Data from forms submissions with method="GET"
  • Pagination
  • JSON-API Allows:
    • include=associationName
    • fields=id,a,b
    • sort=name,-age

Other meta information usualky found in Headers

  • Session
  • Authorization
  • API Versioning
  • Cache busting

BODIES

URL-Encoded, with values Percent-Encoded:

Content-Type: application/x-www-form-urlencoded

name=value&....

Multipart (Usually for File uploads):

Content-Type: multipart/form-data; boundary=---123

---123
Content-Dispostion: form-data; name="emailaddress";

user@example.com
---123
Content-Dispostion: form-data; name="upload"; filename="/path/avatar.png"
Content-Type: image/png

<File Data>
---123--

JSON Payload

Content-Type: application/json

{"meta": {}, "resource":{}}

HTTP RESPONSE

HTTP/1.1 200 OK
Content-Ecoding: gzip
Content-Length: 123
Content-Disposition: attachment; filename="name"
Content-Range: bytes start-stop/length
Content-Type: application/json
ETag: <digest>
Location: <redirected URL>
Link: <url?page=4&per_page=100>; rel="next",
      <url?page=2&per_page=100>; rel="prev",
      <url?page=1&per_page=100>; rel="first",
      <url?page=9&per_page=100>; rel="last"
Set-Cookie: name=value; Max-Age=3600; Version=1

{
  "meta": {},
  "resourceName": {
    "id":"123",
    "attributes": "values...",
    "links": {"association":"value(s)"}
  },
  "links": {},
  "linked": {}
}

HTTP STATUS CODES

Popular ones are:

  • Successful
    • 200 OK
    • 201 Created
    • 202 Accepted
  • Redirection
    • 300 Multiple Choices
    • 301 Moved Permanently
    • 304 Not Modified
  • Client Error
    • 400 Bad Request
    • 401 Unauthorized
    • 402 Payment Required
    • 403 Forbidden
    • 404 Not Found
    • 405 Method Not Allowed
    • 406 Not Acceptable
    • 408 Request Timeout
    • 409 Conflict
    • 410 Gone
    • 416 Requested Range Not Satisfiable
    • 423 Locked
  • Server Error
    • 500 Internal Server Error
    • 501 Not Implemented
    • 503 Service Unavailable
    • 507 Insufficient Storage

HTTP CACHING

Response Headers:

HTTP/1.1 304 Not Modified
ETag: <digest>
Cache-Control: max-age=3600
Expires: Thu, 01 Dec 1994 16:00:00 GMT
Last-Modified: Tue, 15 Nov 1994 12:45:26 GMT

Request Headers:

If-Match: <etag>
If-None-Match: <etag>
If-Modified-Since: Sat, 29 Oct 1994 19:43:31 GMT
If-Unmodified-Since: Sat, 29 Oct 1994 19:43:31 GMT

RACK

Request:

{env}

Response:

[status, {headers}, [bodies]]

This is a dump of the env passed by Rack

{
  "GATEWAY_INTERFACE"=>"CGI/1.1",
  "PATH_INFO"=>"/",
  "QUERY_STRING"=>"",
  "REMOTE_ADDR"=>"127.0.0.1",
  "REMOTE_HOST"=>"localhost",
  "REQUEST_METHOD"=>"GET",
  "REQUEST_URI"=>"http://localhost:9292/",
  "SCRIPT_NAME"=>"",
  "SERVER_NAME"=>"localhost",
  "SERVER_PORT"=>"9292",
  "SERVER_PROTOCOL"=>"HTTP/1.1",
  "SERVER_SOFTWARE"=>"WEBrick/1.3.1 (Ruby/2.1.1/2014-02-24)",
  "HTTP_USER_AGENT"=>"curl/7.24.0 (x86_64-apple-darwin12.0) libcurl/7.24.0 OpenSSL/0.9.8y zlib/1.2.5",
  "HTTP_HOST"=>"localhost:9292",
  "HTTP_ACCEPT"=>"*/*",
  "rack.version"=>[1, 2],
  "rack.input"=>#<Rack::Lint::InputWrapper:0x007f89c3831d10 @input=#<StringIO:0x007f89c3829318>>,
  "rack.errors"=>#<Rack::Lint::ErrorWrapper:0x007f89c3831c98 @error=#<IO:<STDERR>>>,
  "rack.multithread"=>true,
  "rack.multiprocess"=>false,
  "rack.run_once"=>false,
  "rack.url_scheme"=>"http",
  "HTTP_VERSION"=>"HTTP/1.1",
  "REQUEST_PATH"=>"/"
}

Standard CGI Environment Variables:

SERVER_PROTOCOL:      "HTTP/1.1",
REQUEST_METHOD:       "GET",
HTTP_HOST:            "example.com[:80]",
REQUEST_PATH:         "/resource",
QUERY_STRING,         "page=1",

HTTP_USER_AGENT:      "browser",
HTTP_ACCEPT:          "Content-Type,...",
HTTP_ACCEPT_CHARSET:  "charset,...",
HTTP_ACCEPT_ENCODING: "encoding,...",
HTTP_ACCEPT_LANGUAGE: "locale,...",

HTTP_REFERER:         "url",
REMOTE_ADDR:          "127.0.0.1",
REMOTE_PORT:          "63555",

From headers:

Cookies,
Authentication,
X-Special-Headers (execution, response-to),
Range: "pages=1",

From Body:

Post Parameters: {}
Files: {}

Expanded Request:

[METHOD, /api?/version/system/resource,
  { env: {},
    headers: {},
    cookies: {},
    get: {},
    post: {
      name: value,
      name: {record},
      name: {attrib:value, attrib:[value,],},
      name: [{record},],
    },
    files: { name:[file,], name:{
      filename: value,
      mimeType: mime/type,
      encoding: character-set,
      location: uri,
      size: bytes,
    }},
    request: {env + headers + cookies + get + post + files}, // computed, so not official
    authentication: {}, // in headers
    pagination: {}, // in headers
    execution: {}, // in headers
    response: {}, // in headers
  },
  [bodies] // Bodies are irrevelant as this point?
]

RACK/AMQP

Mimic an HTTP Request:

[protocol, basicauth, hostname, port]<-[method, "url?query", {headers}, body]

rack+amqp://server:port/queue.name/resource.format

API Design

AUTHENTICATION

Header

Authentication: token <APIKEY>
Authentication: token <OAUTH-TOKEN>
Authentication: basic <APIKEY-Base64>:<nopassword>

Query or Post Parameter

apikey=<APIKEY>

CONTENT TYPE

Header

Accept: application/json
Content-Type: application/vnd.example.api.v1+json, application/json
Content-Type: application/x-www-form-urlencoded or multipart/form-data

Url

/path/resource.json

VERSIONING

Path

/api/v1/resource

Header

Content-Type: application/vnd.example.api.v1+json

Query or Post Parameter

version=v1

PAGINATION

Query Parameter

GET /resource?page=2&pagesize=100

Link Headers

Link: <url?page=4&per_page=100>; rel="next",

Range Header

Range: bytes=start-stop
Range: pages=1

CLIENT IDENTIFICATION

The Client User agent should be an app name or service making the request.

Headers:

User-Agent: <CLIENT-STRING>
From: <emailaddress>

Or Use an APIKEY that resolves to a user instead of a validatable token

JSON-P

GET /resource?callback=name

returns

/**/name({"meta":{}, "resource":{}, "links":{}})

META Request

Meta can hold things you can't put in HTTP headers (either through a too-simple HTTP client library, or new fields (like execution)

  • Client User agent (should be an app name or service making the request)
  • API Version
  • Execution details
  • Authentication
  • Response (for asynchonous processing)

HTTP Headers for Execution Request

X-Execution: at=time; by=time; retries=n; priority=n; timeout=n;
X-Respond-To: <notification url or message queue name>

META Response

Some Clients don't have easy access to full HTTP headers, so we may want to put some info from those headers here as well:

  • Pagination: "Link": [...] header
  • Status
  • Rate Limiting Information
  • Message
  • Field Messages

For example:

"meta": {
  "status": "200",
  "message": "Successful",
  "fields": {"emailaddress": "Invalid"},
  "Link": ["<url?page=4&per_page=100>; rel='next'",...],
  "execution": {"at":"104984984"},
  "etag": "digest",
  "version": "v1",
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment