A standard for building APIs in JSON
jsonapi.org
jsonapi.org
Let me try to give some perspective on the history. For a long time, JSON API was more of a set of guidelines than a strict specification. A lot of our early adopters hated all of the MAYs in the spec (you can see examples of that throughout this thread), so we decided to tighten things up considerably towards the end of last year.
That meant trying to eliminate the vast majority of options in the primary spec, and moving all optional features to named extensions that clients could programmatically detect.
Of course, significantly tightening up the semantics forced us to grapple with harder problems and pushed some ambiguities into the light of day. RC2 in particular was an attempt to seriously pare down the scope of the main spec, while making the semantics of what was left stricter. Dan (the primary editor) and I spent countless hours discussing various details, and people contributed hundreds and hundreds of (very useful!) comments during this period about various details.
RC3 was a smaller delta, but I could easily imagine that one of the changes had a large impact on existing APIs.
My overall goal for the project from the beginning was to nail down a full protocol (both the format and the wire protocol) that could be implemented by multiple languages on both sides. Originally, it was created because I was frustrated by the ambiguity of "REST-style API" was during the development of Ember Data.
The earliest versions of JSON API didn't really nail things down well enough to deliver on that promise, but I hope that the latest versions will be able to. Time will tell.
One challenge with the churn is a lack of any high-level changelog (git commit history doesn't count). I have a team working off an earlier version of the spec, and I check back semi-frequently. But I haven't been able to find a document outlining "here are the major changes since the previous versions." I understand that would represent more work on top of work, but for such a large spec, the changes have been disorienting.
My main issue was with the "related/linkage" stuff (which may or may not have changed somewhat since I was exposed to it), which is unpleasant, to my eyes, in the way it is expressed, and also verbose and often duplicative in what it produces.
A related problem was the Node.js implementation we were using, which generally felt like more of a hindrance than a help (and which we ended up extending beyond all recognition, making matters dramatically worse - though that was no fault of JSON API itself directly, other than by association).
Best of luck!
Here's the basic things that have been mostly useful:
Maybe x, for optional parameters.
Process by defining what to do when null.
Doesn't have to be tagged to be useful.
Repetition in the form of [x] and Map String x.
Process only via a for-each loop.
Tagged-sums-of-products: ["tag", arg1, arg2, ...]
Process only with a switch statement dispatching over arr[0].
Record types like {"id": 3, "name": "Gandalf", "color": "grey"}
Feel free to base logic on record.name etc.
Primitives: dates, ints, strings, blobs.
The tagged-sums bit is probably a dealbreaker for me at this time: I picked this up from Haskell and I would not easily let go. The syntax in JSON is a little clumsy, but it's still important. Cf. Abelson and Sussman's lectures, "All interesting programs start with a case dispatch."They keep saying that they're closing in on 1.0, but there's a few issues left:
https://github.com/json-api/json-api/issues?q=is%3Aopen+is%3...
They've been making a lot of changes lately:
https://github.com/json-api/json-api/graphs/contributors
It's good that they want to get all of the breaking changes done so that they can declare a stable 1.0. But it also seems like they're going to try and call it stable right after making a bunch of changes, which seems risky.
I also pitched JSON-LD/Hydra at work, because they're w3c-backed and JSON-LD has some uptake. But the other people who looked at those specs found them hard to digest. And I agree; as an implementer, I can read the jsonapi docs quickly and have a pretty clear idea of what to do. But with JSON-LD/Hydra, not so much.
I get the sense that JSON-LD/Hydra is more flexible than jsonapi, but I think jsonapi does what we need. And if it does what we need, then additional flexibility might actually be a drawback. I guess we'll see how it goes.
For what it's worth, the issues you linked to are mostly about adding more rigor to possibly underspecified areas, not changing things that are already specified, but those things could easily be done after 1.0.
The good:
- It opens a path for standardized tooling among API clients. Rather than having a whole mess of JSON-over-HTTP clients with a hard-coded understanding of the particular API, one could theoretically use a hypertext client to interact with any API using this standard.
- It establishes a baseline of what an API server must implement.
The bad:
- It tries to control not just the media type, but the protocol (HTTP) and server implementation. This is problematic because it dictates how your server must implement its routes, and is tightly coupled with HTTP.
- It tries to be very prescriptive, but it cannot cover all edge cases without being exhaustive. This comes at a heavy burden for implementers to get every part of the specification correct. Despite this, some extremely basic features are missing, such as providing an index document so that a client can enter the API knowing only a single entry point (as it stands now, clients must have a priori knowledge of what resources exist on the server).
The ugly:
- Since so many parts of the base specification are optional MAYs, and there is no prescribed mechanism for feature detection, there is no way to figure out if a server supports a particular feature of this spec without trying to do something and failing.
- The spec has made many breaking changes on the road to 1.0 (as other commenters have mentioned, and there is still room for breaking changes).
At this point, I think that this project gained traction due to the clout of the original authors (Yehuda Katz and Steve Klabnik) and the promise of no-more-bikeshedding, though I would argue that the bikeshedding has just shifted from particular APIs to the spec. Disclosure: I authored and maintained some libraries that implement this spec, and authored another media type for hypertext APIs.
Some media types do consider their applicability over multiple protocols, for example: http://amundsen.com/blog/archives/1151
> The spec has made many breaking changes on the road to 1.0
Interestingly, most of the breaking changes on the road to 1.0 were about drastically reducing MAY in favor of MUSTs.
Optional features were moved to named extensions, and there is now an explicit way to negotiate about those extensions.
For anyone who was put off earlier on by the number of MAYs, know that we heard you loud and clear. It may (no pun intended) be worth another look.
This holds true for small projects, but as soon as you are working within a large, complex system that involves multiple teams, the benefits of using standards pays off.
I struggled in the past working on projects with companies that had built dozens of loosely-defined APIs, built with the good design intentions in mind, but suffered later from incomplete or inconsistent implementations. The app codebase became much fatter, in an attempt to abstract away those differences into a consistent mental model.
When complexity and communication reaches a certain threshold, it makes sense to invest in standardizing the APIs and responses. I've seen the payoff and am convinced: client libraries and frontend implementations get much simpler, documentation becomes easier, and discussions about how to make changes or design new APIs all but go away.
On the other hand, for small teams and simple services, using a standard like this is probably overkill, unless everyone involved is used to doing APIs this way.
That depends a lot on the nature of the project and the standards involved. In my experience, introducing complexity early on in the hope that it will pay off when the project itself becomes complex, leads to exactly what you would intuitively expect: a huge combinatorial code nightmare where productive work asymptotically approaches zero as time progresses.
There is a failure mode in the development of enterprise projects where actual work is being done only at the fringes where the tight external standards have loopholes allowing for the introduction of actual functionality through the backdoor. The resulting systems are of course extremely brittle.
> for small teams and simple services, using a standard like this is probably overkill
There are also simple standards deserving of the name. Small teams and simple services use them quite adamantly.
The "problem" I see with JSON API is not one of complexity (it really isn't, very), it's specificity. It's designed to cover a good range of common web-centric data interchange problems, but like any higher-order standard it carries the weight of certain abstractions and concepts. The pain comes, in my opinion, not from a project/team size mismatch but in cases where these abstractions are ill-suited for the problem at hand.
One of the key factors why plain JSON has become so popular: it's completely malleable. As a result, the actual JSON data on the wire can closely reflect the internal data structures (or at least a logical representation of them) of producers and consumers. The price for this is a relatively tight coupling, but the pain of it is lessened somewhat by the simplicity of the format.
In the end, the old adage of the structure of the project mirroring the structure of the organization probably holds true. When selecting a standard to work with, people choose one that innately reflects how their company works, and they do it for good reason: to reduce friction.
I'm not a fan of the spec, but overall it seems to strike a good balance between what is specified and what is left out. To compare this to SOAP is missing the point.
What killed SOAP was an insistence on treating HTTP as a dumb transport - thereby breaking the constraints of the interwebs, the inherent brittleness of RPC, and the lunacy of the WS-* stack.
None of that applies here, it's closer in spirit to AtomPub, which is still a pretty decent standard, but just happens to be in XML which everybody hates nowadays.
I think a lot of the commentators in this thread seriously believe that having a different data format and interaction style for every API on the internet is somehow "simpler" than adopting a loosely defined set of conventions and writing them down somewhere as a standard.
My stab at imposing some consistency on data APIs boils down to a header and a data section. The header's utility is in describing the data, most obvious utility comes from including a "count: 235" or paging data.
Less-obvious is having self-describing data, namely including the path and query parameters in the header, so you could ingest the data sans request and still know what it represents.
But it's a little bikesheddy, and that might be that data-only APIs are so freaking simple that no standard is really necessary. If so, I must question writing a standard around how we happen to build GUIs today as it seems doomed to SOAPy failure. But hey I'm not "full-stack" ...
For example, RC2 mandated that all fields were dasherized. We made field names opaque in RC3.
At this point, we're pretty much nailing down tiny details and included this language with RC3:
JSON API is at a third release candidate state. This means
that it is not yet stable, however, libraries intended to
work with 1.0 should implement this version of the
specification. We may make very small tweaks, but
everything is basically in place.https://en.wikipedia.org/wiki/Software_release_life_cycle#Re...
We also should not confuse an API with a transport protocol. I will tilt my hat to this not being as verbose as past attempts, but why reinvent the wheel yet again? It's not like prior attempts didn't function as expected - they did - but we in the industry chastised them for being too strict.
Let's work on improving the semantics and documentation around what constitutes an API. Swagger is an excellent example of this.
https://developers.google.com/protocol-buffers/docs/proto#se...
This tries to standardize API transports, which doesn't make a sense to me, because there is no need for that. If you are going to develop an API with (open) SDK clients that support consuming your services, then I don't need to care for your API transport to be written in JSON-API. Especially making transports human readable appears to be a waste of ressources in my eyes. Those API's are meant to be consumed by machines and debugging can and should be done with tools, not by enforcing a standard.
Edit: and hey, why not, I'm the author of a generic HAL client myself: https://github.com/deontologician/restnavigator
Consider the case of a blog. Each blog post has many comments. Each post has an author, and each comment has an author. Some of those entities may have dedicated URLs, and others may not. Additionally, the authors are highly repetitive; you want to refer to them by identifier, not by embedding them.
Because a tree of data can be represented easily as a graph, but not vice versa, JSON API provides a straight-forward way to provide a list of records that are linked to each other. The goal is simple, straight-forward processing regardless of the shape of the graph.
As an example, let's say that my graph DB has People nodes and a MARRIED_TO relationship between two people to indicate they are married. A MARRIED_TO edge could have a "married_on" property containing the date of the marriage.
Where would the "married_on" attribute be represented in JSON API? I could stuff it in the "meta" member of the link object, but that feels loose and hacky. Maybe it could live in the "linkage object" along with the "type" and "id" members. But the linkage object appears to currently be a closed set of only those two members.
Is this requirement to present a full property graph model not as common as I would imagine? I'm a bit behind on my sync with the current state of the spec. This is the first time I've tried to elaborate this need.
Most of the trouble in building a JSON API comes not from deciding on the format, but in nailing down the precise requests and responses that handle common kinds of interactions. This became very clear to me as I worked on Ember Data.
See http://de.slideshare.net/lanthaler/building-next-generation-...
But my "fear" is that it will all get to complicated, too. Currently I really like to work with JSON Schema (http://json-schema.org/), because it's simple and extensible.
Make your API consistent and write decent documentation for it. That's all anybody needs and will be simpler then trying to conform to some insane metastandard.
You can have a look of a simple way to do it in php with symfony : https://github.com/dunglas/DunglasJsonLdApiBundle
1. The format should pass a basic JSON linter.
2. (where applicable) The document should represent the logical object type that you'd expect from the endpoint.
Anything beyond this is getting too close to XML for my tastes.
First of all, JSON API provides guidance for a lot of API design decisions that are usually contentious because, although they may seem trivial, they must be made with care in order to be consistent and viable. For instance, JSON API provides guidance for:
* fetching related resources together with primary resources in order to minimize requests
* limiting the fields included in a response document in order to minimize payload size
* paginating data with links that work well with any pagination strategy (page-based, offset-based, and cursor-based)
* sorting results, even potentially based on related resource fields
* representing heterogeneous collections and polymorphic relationships
* representing errors
In my opinion, some of the most useful guidance is related to the representation of relationships, which can include:
* embedded linkage data which "links" primary resources with related resources in a compound document.
* relationship URLs which can retrieve linkage data and directly manipulate the relationship without affecting the underlying resources.
* related resource URLs which can retrieve related resources themselves.
In addition, JSON API supports extensions and has official extensions for performing bulk updates and JSON Patch operations. These particular extensions provide extremely useful mechanisms for transactionally working with multiple resources.
All of this guidance has been forged over the course of two years based on the feedback and contributions of hundreds of developers. Even if you were to incorporate some of this guidance in your APIs piecemeal, it would probably save your team many hours of design discussions.
However, the bigger value proposition of JSON API is just now beginning to be realized. Since we've tightened the "spec" into a proper spec by eliminating many MAYs and SHOULDs, it is now possible to reliably build implementations with a guarantee of compatibility. It's regrettable that it took us so long to move from providing loose guidelines to a more rigid spec, but I truly believe that the awkward intermediate phase provided invaluable feedback that ultimately informed the design of the spec and will improve adoption of 1.0.
We are seeing client libraries being built in JavaScript, iOS, and Ruby, and server libraries in PHP, Node.js, Ruby, Python, Go, and .NET. Although not all are (yet) compliant with the latest changes to the spec, we are tracking progress carefully as the spec nears 1.0 to ensure that we smooth over any rough spots encountered by implementors. I'm personally involved in developing Orbit.js and JSONAPI::Resources, both of which are nearing JSON API compliance.
I can say that it's incredibly satisfying to build applications with compatible client and server libraries. It lets me focus on the design of my application's domain instead of myriad details related to transforming data representations and protocol usage. Even better is the knowledge that other clients can easily use my API and all of its advanced features, regardless of their language and framework. This is ultimately the promise of JSON API, and it won't be long before it's realized.
A switch to JSON-RPC 2.0 meant everyone now understood the entirety of the specifications our APIs relied on, and if we couldn't find a good implementation for our target, we could either fix one, or write one ourselves in just a few hours.
The productivity gain was effectively infinite.
I look at the JSON API specification in all its architecture astronaut glory, and weep.
Its hard to take the API seriously when they fk up some of the best parts of the json spec.
No, thanks.
About the only thing useful in the spec is a separate errors list so that I can easily check if it worked as it should.
Also there is apparently no way to authenticate so you either have to allow everybody to create/alter whatever on your server or nobody. Yikes.
If I get my way, whatever I work on will be tailored to break if the client assumes this "standard".
The result MUST be in JSON format.
Documentation of the API MUST exist.
Errors SHOULD change the HTTP code as appropriate
That is all.
JSON/REST are beloved because it's simple and just works. Originally XML itself was simple too with just DTD as "Schema".
BUT then all the additional standards like XMLSchema, XSLT, XML-RPC/SOAP turned it in an ugly duck.
JSON API is extracted from the JSON transport implicitly
defined by Ember Data's REST adapter.
http://jsonapi.org/about/
As JSON-API comes from the Ember.js camp, I remember the Ember vs. Angular discussions on HN 1-2 years ago. Google Trends shows Ember.js has gone nowhere. https://www.google.com/trends/explore#q=AngularJS%2C%20Ember... (and now there is also React). So I guess the "Ember.js way" to do things failed. At some point we should learn from history, and not turn JSON into SOAP/WSDL - https://xkcd.com/927/ .XML-RPC/SOAP and/or many other remote procedure call transport techniques failed in in the long term and are a big pile of legacy mess (CORBA, DCOM, RMI, SAP RFC, .NET Remoting, XML-RPC, etc.): http://en.wikipedia.org/wiki/Remote_procedure_call
But I definitely see some benefits with json-api. Here are two:
1. When writing large single-page apps in JS, client <-> backend communication will need some structure. Could be a few project-internal guidelines, or something like json-api.
2. Client frameworks and backend frameworks are often separate projects (e.g. Django/express/sinatra server-side or React/Backbone/Mithril client-side). It's helpful with a standard to converge on for authors of backend json/rest serializers, and data adapters client-side.
I'm not expert of RPC's, but I'm pretty sure it's something quite different from jsonapi.
There is no consensus on the details of "REST". Every implementation has its subtle quirks and solutions to the same basic problems. Every client library must be customized to adapt to these differences. Since the fundamental aspects aren't agreed upon, there is little chance of defining compatible features at a higher level.
On the other hand, the value proposition for JSON API is rooted in consensus [1]
[1] https://news.ycombinator.com/item?id=9281102
> As JSON-API comes from the Ember.js camp
JSON API has been proven in many languages and frameworks. The JSON API team is diverse and has only one member on the Ember core team (@wycats). More importantly, JSON API has been influenced by hundreds of contributors with diverse backgrounds and specialties.
At this point, JSON API is pretty far from its original extraction from Ember Data. It has come full circle to the point that a JSON API Adapter is being written from scratch for Ember Data (and is now almost fully compatible).
What I am concerned mostly is the added overhead of fitting everything under a single standard. Sometime you need state aware api, sometime you need data retrieval api, sometime you need to expose a remote interface to constraint manipulating objects with server side rules.
Why it all has to be a standard escapes me. You mostly get absurdly convoluted operation when you try to fit one model under another.
> JSON API has been proven in many languages and frameworks. The JSON API team is diverse and has only one member on the Ember core team (@wycats). More importantly, JSON API has been influenced by hundreds of contributors with diverse backgrounds and specialties.
CORBA (one of the cited failed examples above) is notable for having been rooted in consensus. It pulled together dozens of use-cases from all over industry... and became a giant, hulking, overly complex, unpleasant-to-use mess.
here is the trend for "ember.js vs angular.js" http://www.google.com/trends/explore#q=ember.js%2C%20angular...
I guess the point is that google trends is often a bad metric.
I'm not sure about the server-side implementation, but using it client-side was a breeze. I managed to completely automate my web-service calls to automatically parse/generate the required JSON and update the resources client-side.
Then, all I had to do to interact with new resources was register resources client-side with it's mappings.
@interface ModelResource : NSObject
+ (NSString)resourceName; + (NSString)resourcePluralName;
- (void)createMappings;
- (void)addAttributeMapping:(NSString)attributeName toProperty:(NSString)propertyName;
- (void)addLinkMapping:(NSString)linkName toProperty:(NSString)propertyName withResourceName:(NSString)resourceName;
- (void)fromJSON:(NSDictionary)jsonData; - (NSDictionary)toJSON;
@end
@interface UserModel : ModelResource
@property (nonatomic, copy) NSString fullName; @property (nonatomic, copy) NSString firstName;
@property (nonatomic, weak) EmailModel emailAddress;
@end
@implementation UserModel
+ (NSString )resourceName { return @"user"; }
+ (NSString )resourcePluralName { return @"users"; }
- (void)createMappings { [super createMappings];
[self addAttributeMapping:@"fullName" toProperty:STR_PROP(fullName)];
[self addAttributeMapping:@"firstName" toProperty:STR_PROP(firstName)];
[self addLinkMapping:@"emailAddress" toProperty:STR_PROP(emailAddress) withResourceName:[EmailModel resourceName]];
}@end