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
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
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: [email protected]
Range: bytes=start-stop
User-Agent: <agent string>
<body>
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
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";
[email protected]
---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/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": {}
}
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
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
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?
]
Mimic an HTTP Request:
[protocol, basicauth, hostname, port]<-[method, "url?query", {headers}, body]
rack+amqp://server:port/queue.name/resource.format
Header
Authentication: token <APIKEY>
Authentication: token <OAUTH-TOKEN>
Authentication: basic <APIKEY-Base64>:<nopassword>
Query or Post Parameter
apikey=<APIKEY>
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
Path
/api/v1/resource
Header
Content-Type: application/vnd.example.api.v1+json
Query or Post Parameter
version=v1
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
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
GET /resource?callback=name
returns
/**/name({"meta":{}, "resource":{}, "links":{}})
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>
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",
}