API Description Language / Client library generator (related: top-level document keys spec?)


#1

My biggest question is: Now that you’ve made an awesome API that serves JSON-API, how do you generate client libraries?

I’m loving JSON-API so far. Unfortunately one of the requirements for my project is to somehow describe the API using a machine-readable format, mostly so that client libraries can be automatically generated, although as a plus this would take care of human-readable documentation. That’s not such a big concern because I’m using Rails and rspec_api_documentation provides great documentation. I need a client library ideally in Scala, but Java would suffice. I see that there are no Java or Scala clients.

So it looks like for describing the API, Swagger might be my best bet. I believe that there is little documentation on describing JSON API using Swagger, and one of the reasons for the lack of docs might be that it’s hard or practically impossible to make a halfway decent description of it. There’s the Swagger Codegen which supports a lot of languages, so making a swagger spec would be a huge win.

With JSON:API, the included top-level key presents the biggest obstacle here, because it’s an array of disparate types. No matter how I try to do this, including using the discriminator feature of Swagger (which Swagger Codegen doesn’t support, apparently), I can’t get an accurate description or useful client library. A kludge would be to make additional top level keys like included_[type1] and included_[type2] – the spec says that the root-level document MUST include some things, and MAY include some other things, and there are some mutually exclusive ones (like data and errors) but doesn’t explicitly specify that it MUST NOT include other keys.

Can someone clarify whether or not the top-level document MUST NOT include keys that are not specified in the spec?


#2

Can someone clarify whether or not the top-level document MUST NOT include keys that are not specified in the spec?

Technically, no, the server must not send keys that aren’t defined in the spec. (Even though, if such a key is sent, the client must ignore it rather than rejecting the document.) This is to allow the spec to define new top-level keys over time, without breaking existing clients. If any API were allowed to add custom keys at the top-level, then extending the spec would be impossible, because a key added to the spec could conflict with that key being used in the wild.

The relevant text is this sentence:

Unless otherwise noted, objects defined by this specification MUST NOT contain any additional members

Still, I understand your problem and you’re not the first person to mention that the polymorphic nature of the "included" key can make it difficult to represent with schemas or in strictly typed languages. We’re actively working on a solution!

Until then, my suggestion would be to deviate from the specification on this one point. That is, make custom top-level keys as you suggested and make a custom query parameter (e.g. ?include-typed) that controls their contents. In practice, such a deviation won’t cause any problem, and it’ll still give you and your client devs the benefits of JSON API libraries for everything but the ?include feature.

A couple small points:

  1. For the custom top-level keys, try to pick a specific name, such that it’d be very unlikely for the spec to define such a key in the future. "included_articles" etc. are pretty good, though if you’re comfortable doing something even more unique, that wouldn’t hurt.

  2. Make sure that, if the standard ?include parameter is provided, you return a 400 (which is totally allowed, and signals to clients that you don’t support standard includes). Also, make sure that, whatever name you pick for your custom query parameter, it includes at least one non a-z character (again, to prevent a conflict), like ?include-typed.

Hopefully, we’ll have a more elegant solution soon!


#3

Ethan, thanks for your response. The guidance you’ve given here is useful, and I bet others would benefit when they run into a case where they’re considering custom top-level keys. Would it be useful to describe a standard for non-standard keys? For instance, a blurb that explains the rationale (as you have), and recommends namespaced keys or such? Perhaps myapp_included_typed for this case.

Could you also shed some light on the options you’re considering to handle the polymorphism of this particular case? It would be useful to build towards something like what you’re thinking.