Skip to content

Instantly share code, notes, and snippets.

@afair
Last active December 21, 2015 01:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save afair/6225435 to your computer and use it in GitHub Desktop.
Save afair/6225435 to your computer and use it in GitHub Desktop.
JSON Messages - From a Philly.rb lightning talk I gave on August 13, 2013.
var talk = {
title: "JSON Messaging: From REST and Beyond",
by: { name: "Allen Fair",
twitter: "@allenfair" },
event: { organization: "Philly.rb",
when: "August 12, 2013 23:00 UDT",
style: "☇ Lightning" },
href: "https://gist.github.com/afair/6225435"
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Do we need a standard method of JSON Request/Response passing?
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var requestMediums = [
"HTTP+REST+Hypermedia",
"AJAX",
"JS Frontend (Backbone/Ember/etc.)",
"Mobile Apps",
"Message Queueing",
"node.js",
"SOA - Service Oriented Architecture",
"IPC & RPC - Interprocess Communication, Remote Procedure Calls",
];
var standardRequest = {}; // ?????
var standardResponse = {}; // ?????
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// REST - Resource (Record) CRUD over HTTP
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var rest_call = { // HTTP as envisioned at JSON
method: "GET", // GET POST PUT/PATCH DELETE
uri: "http://example.com/post/1",// Resource identifier
uriParams: {search:"terms", page:1}, // For GET, Optional
headers: {Accept: "application/json" }, // + cookies
postParams: {fieldName:"Value"} // For POST, PUT, PATCH
postFiles: {fileName:"Data"} // For POST, PUT, PATCH -> Multipart
}
// NOTE: Request embedded in HTTP Protocol, Data in POST Body
var http_response = {
httpStatus: 200, // Look here for success
httpMessage: "200 Ok"
headers: { "Content-Type": "application/json" },
body: "{post:{id:1, title=\"First Post\"}}" // Just the requested data?
};
var result = JSON.parse(http_response.body);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Recommended Rails JSON Response -- circa 2009
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var rails = {
uri: "http://paydrotalks.com/posts/45-standard-json-response-for-rails-and-jquery/",
author: "Peter Bui"
response: {
status: "ok|redirect|error",
to: "http://www.redirect-url.com",
html: "<b>Insert html</b>",
message: "Insert some message here"
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JSEND - "How JSON responses from web servers should be formatted"
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var jsend = {
uri: "http://labs.omniti.com/labs/jsend",
jsendResponse: {
status: "success", // success, fail, error (exception)
data: {post: { id:1, title: "First Post" } }
},
jsendFailResponse: { // Request Error
status: "fail",
data: { title: "Post not found" } // user-defined failure object
}
jsendErrorResponse: { // Exception
status: "error",
message: "Could not connect to the database"
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ODATA -- the product of a deranged Java Architect
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var odata = {
uri: "http://www.odata.org/documentation/json-format",
serviceDocument: {
d : { EntitySets: ["Products", "Categories", "Suppliers"] } },
collectionResult: {
d : {
results: [ {
__metadata: {
uri: "http://services.odata.org/OData/OData.svc/Categories(0)",
type: "DataServiceProviderDemo.Category" },
ID: 0, Name: "Food", // <=== REQUESTED RECORD
Products: { // Included Association
collectionResult: {
d : {
results: [ {
__metadata: {
uri: "http://services.odata.org/OData/OData.svc/Categories(0)",
type: "DataServiceProviderDemo.Category" },
ID: 0, Name: "Food", // <=== REQUESTED RECORD
Products: {
__deferred: {
uri: "http://services.odata.org/OData/OData.svc/Categories(0)/Products"
} ,
},
} ],
__count: 2,
__next: "http://services.odata.org/OData/OData.svc$skiptoken=12"
}
},
entryResult: {
d : {
results: {
__metadata: {
uri: "http://services.odata.org/OData/OData.svc/Categories(0)",
type: "DataServiceProviderDemo.Category"
},
ID: 0, Name: "Food", // <=== REQUESTED RECORD
Products: { // Association data not included
__deferred: {
uri: "http://services.odata.org/OData/OData.svc/Categories(0)/Products"
}
}
}
}
}
}
} ]
}
}
};
//------------------------------------------------------------------------------
// HAL/HATEOAS: A uniform interface for REST
// "REST APIs must be hypertext-driven" -- Roy Fielding
//------------------------------------------------------------------------------
var hal = {
name: "Hypertext Application Language",
alsoSee: "HATEOAS: Hypermedia as the Engine of Application State",
uri: "http://stateless.co/hal_specification.html",
mediaType:"application/hal+json",
collectionResult: {
_links: {
self: { href: "/orders" }, // self is current URI
next: { href: "/orders?page=2" }, // pagination
find: {
href: "/orders{?id}",
templated: true
},
admin: [
{ href: "/admins/2", title: "Fred" },
{ href: "/admins/5", title: "Kate" }
]
},
currentlyProcessing: 14, // Data?
shippedToday: 20,
_embedded: {
order: [ // Associations?
{
_links: {
self: { href: "/orders/123" },
basket: { href: "/baskets/98712" },
customer: { href: "/customers/7809" }
},
total: 30.00,
currency: "USD",
status: "shipped"
},
{
_links: {
self: { href: "/orders/124" },
basket: { href: "/baskets/97213" },
customer: { href: "/customers/12369" }
},
total: 20.00,
currency: "USD",
status: "processing"
}
]
}
}
};
//------------------------------------------------------------------------------
// Hypermedia API / Collection+JSON / Roar
//------------------------------------------------------------------------------
var roar = {
about: "Roar gem for Hypermedia API's",
uri: "https://github.com/apotonick/roar",
basedOn: "http://amundsen.com/media-types/collection/format/"
singletonResponse: {
article: {
title: "Lonestar Beer",
id: 4711,
links: [
{ rel: "self", href: "http://articles/lonestarbeer" }
]
}
}
collectionResponse: {
collection: {
version : "1.0",
href : "http://songs/",
items : [
{ href : "http://songs/scarifice",
data : [
{name: "title", value: "Scarifice"},
{name: "band", value: "Racer X"}
],
links : [
{rel: "band", href: "http://bands/racer-x"}
]
} // , ...
]
}
}
};
//------------------------------------------------------------------------------
// Siren: a hypermedia specification for representing entities
//------------------------------------------------------------------------------
var siren = {
uri: "https://github.com/kevinswiber/siren",
contentType: "application/vnd.siren+json",
response: {
class: [ "order" ],
properties: {
orderNumber: 42,
itemCount: 3,
status: "pending"
},
entities: [
{ class: [ "items", "collection" ],
rel: [ "http://x.io/rels/order-items" ],
href: "http://api.x.io/orders/42/items"
},
{
class: [ "info", "customer" ], // Class
rel: [ "http://x.io/rels/customer" ], // Location
properties: { // Attributes
customerId:"pj123",
name: "Peter Joseph"
},
links: [ // Links
{ rel: [ "self" ], href: "http://api.x.io/customers/pj123" }
]
}
],
actions: [ // Set of HTML-form-like actions to send
{
name: "add-item",
title: "Add Item",
method: "POST",
href: "http://api.x.io/orders/42/items",
type: "application/x-www-form-urlencoded",
fields: [
{ name: "orderNumber", type: "hidden", value: "42" },
{ name: "productCode", type: "text" },
{ name: "quantity", type: "number" }
]
}
],
links: [
{ rel: [ "self" ], href: "http://api.x.io/orders/42" },
{ rel: [ "previous" ], href: "http://api.x.io/orders/41" },
{ rel: [ "next" ], href: "http://api.x.io/orders/43" }
]
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JSONAPI.org -- From Steve "Hypermedia" Klabnik and Yehuda "Ember" Katz
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var jsonapi = {
uri: "http://jsonapi.org/",
idDocument: { // ID-Based Document
meta: { // Optional, not defined
pagination: {} // Mentioned
},
posts: [
{ id:1,
title: "First Post",
links: {
author: "9",
comments: [ "5", "12", "17", "20" ]
}
}
],
},
urlDocument: { // URL-Based Document
meta: { // Optional
"client-ids": true, // See Client-Side ID's
},
posts: [
{ id:1,
title: "First Post",
links: {
author: "http://example.com/people/1",
comments: "http://example.com/comments/5,12,17,20"
}
}
],
},
urlTemplate: {
links: {
"posts.comments": "http://example.com/posts/{posts.id}/comments"
},
posts: [
{ id: "1", title: "Rails is Omakase" },
{ id: 2, title: "The Parley Letter" }
]
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JSON-RPC - Stateless light-weight remote procedure call protocol
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var jsonRpc = {
url: "http://www.jsonrpc.org/specification"
request: {
jsonrpc: "2.0", // Version number
method: "methodName", // Method name to be invoked
params: ['parameter'],// Options Parameter Structure, [] or {}
id: "identifier" // Client Identifier (string, integer, NULL)
}, // NULL is notification, no response requested
response: {
jsonrpc: "2.0", // Version number
result: "value", // Only for success, can be [] or {}
error: { // Only for error
code: 123, // Integer code for error
message: "error message",
data: "stuff" // Primitive or Structure ([] {}) from server
},
id: "indentifier" // Same as request id
}
batchRequest: [ request1, request2 ],
batchResponse: [ response1, response2 ] // Unless notification request batch
};
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// SOAPjr - a hybrid of SOAP and JSON-RPC (SOAP, not WSDL)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var soapJr = {
uri: "https://github.com/gitpan/SOAPjr",
request: { // basic request to view the jCard details for a single user
HEAD: {
service_type: "contacts",
action_type: "view",
sid : "80e5b8a8b9cbf3a79fe8d624628a0fe5"
},
BODY: {
username: "jbloggs"
}
},
response: {
HEAD: {
result: "1"
},
BODY: {
email: [
{ type: ["internet","pref"], value: "spam@SOAPjr.org" }
],
fn: "Joe Bloggs",
kind: "individual",
n: {
"family-name":["Bloggs"],
"given-name": ["Joe"],
value: "Bloggs;Joe"
},
org: [
{ "organization-name": "SOAPjr.org" }
]
}
},
soapJsonRequest: { // Example SOAP Request translated to JSON
HTTP: { // HTTP, SMTP, TCP, or JMS/Message Queue
url: "POST /InStock HTTP/1.1",
Host: "www.example.org",
ContentType: "application/soap+xml; charset=utf-8",
SOAPAction: "http://www.w3.org/2003/05/soap-envelope"
}
Envelope: { // Like SMTP Delivery
Header: {}
Body: {
GetStockPrice: { // Method
StockName:"IBM" // Parameter
}
}
},
soapJsonResponse: {
Envelope: {
Body: {
GetStockPriceResponse: {
GetStockPriceResult: "....." // Requested Data
OutputParam: ["value"] //if any returned
}
}
}
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// JSON Patch (JSON & HTTP Patch) & Rocket
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var jsonPatch = {
url: "http://tools.ietf.org/html/rfc6902",
url2: "http://rocket.github.io"
httpRequest: {
method: "PATCH", // or SUBSCRIBE for Rocket
url: "/my/data",
contentType: "application/json-patch+json"
body:
[
{ op: "add", path: "/a/b/c", value: [ "foo", "bar" ] },
{ op: "remove", path: "/a/b/c" },
{ op: "replace", path: "/a/b/c", value: 42 },
{ op: "move", from: "/a/b/c", path: "/a/b/d" },
{ op: "copy", from: "/a/b/d", path: "/a/b/e" },
{ op: "test", path: "/a/b/c", value: "foo" }
]
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Google JSON Style Guide for Requests and Responses
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var googleJsonGuide = {
url: "http://google-styleguide.googlecode.com/svn/trunk/jsoncstyleguide.xml"
request: "GET http://google.com?q=json+request+standard",
response: {
apiVersion: "2.1",
id: 1,
data: {
query: "json request standard",
time: "0.1",
currentItemCount: 10,
itemsPerPage: 10,
StartIndex: 11,
totalItems: 2700000,
nextLink: "http://www.google.com/search?hl=en&q=json+request+stardard&start=20&sa=N",
previousLink: "http://www.google.com/search?hl=en&q=json+request+stardard&start=0&sa=N",
pagingLinkTemplate:"http://www.google.com/search?hl=en&q=json+request+stardard&start={index}&sa=N",
},
items: [
{ title: "Jsend",
// ...
} // , ...
]
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Composite JSON Request/Response Documents
// - Use only what you need and what makes sense in the transport protocol
// - Map request to protocol (HTTP, RPC, Queue, etc.)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var jsonRequestResponse = {
request: { // ClassName.find(id).method_name(*arguments)
meta: { // Maps to HTTP Headers & URL Params
apiVersion: "1.0",
clientName: "MyClient v1.3",
clientId: "sessionId|Address|UUID",
requestId: "UUID|etc.",
requestType: "synchronous|asynchronous|notification", // Respond, queue, or no response required
execution: {at:"timestamp", by:"timestamp", retries:10, priority:1, attempts:0, timeout:10},
authentication: {method:"loging|digest|oauth", key:"x", user:"x", password:"x"},
notification: [{service:"name", uri:"...", message:"..."}],
pagination: {page:1, size:10}, // For Collections
},
// JSON-RPC like request, mappable to REST
// POST http://example.com/post/123/approve
uri: "path|class|name", // What
id: "identifier", // Which
method: "methodName", // HTTP Methods, Soap-Style OO Methods
arguments: [], // Arguments to method
data: {} // For a data push, not process request
},
response: {
meta: {
apiVersion: "1.0"
status: "success|error|exception",
httpStatus: 200,
httpMessage: "200 Ok",
redirectTo: "http://redirected.com",
execution: {at:"timestamp", duration:1.2, cost:1.23},
message: "Okay",
errors: {fieldId:"Missing"},
pagination: {page:1, size:10, pages:10, records:100},
eTag: "686897696a7c876b7e" // Or any addition HTTP-Like header
},
post: { id:1, title:"First Post" }, // Entity
comments: [ { postId:1, text:"LOL" } ], // Collection, has-many association, etc.
links: {
self: "http://example.com/posts/1",
all: "http://example.com/posts/",
page: "http://example.com/posts/?page={page}",
first: "http://example.com/posts/1",
previous: null,
next: "http://example.com/posts/2",
last: "http://example.com/posts/100",
// Associations
author: "http://example.com/author/1",
comments: "http://example.com/posts/1/comments"
}
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// CREDITS
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var references = [
"http://stackoverflow.com/questions/12806386/standard-json-api-response-format"
];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment