Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add best practices for building Ruby JSON APIs #365

Closed
wants to merge 6 commits into from

Conversation

malandrina
Copy link

* Recommend that readers follow Heroku's HTTP API Design Guide when
  developing JSON APIs
* Prefer `oj` for parsing JSON and RSpec request specs for integration
  testing, per iOS on Rails
* References:
  https://github.com/thoughtbot/ios-on-rails/blob/master/book/rails/introduction.md#parsing-incoming-json-requests
  https://github.com/thoughtbot/ios-on-rails/blob/master/book/rails/creating_a_get_request.md#it-all-starts-with-a-request-spec
@drapergeek
Copy link
Contributor

Do you think it's time we choose a side for JSON API or not?

@malandrina
Copy link
Author

@drapergeek I haven't used JSON API yet so I have no opinion! Might make sense for adding JSON API to be its own PR.

@drapergeek
Copy link
Contributor

Might make sense for adding JSON API to be its own PR.

I wasn't actually recommending we back it 😄
I think we should have some stance, whether it's for or against.

I'll open a PR once you start this and see what people have to say.

@BlakeWilliams
Copy link
Contributor

I don't think it make sense to be explicitly for or against JSON API since it seems situational. For example it would be great for writing the back-end of an Ember-app, but not so much for other cases like iOS/Android, or other frameworks.

edit: If I had to choose though, I'd choose against.

@jferris
Copy link
Contributor

jferris commented Oct 21, 2015

For example it would be great for writing the back-end of an Ember-app, but not so much for other cases like iOS/Android, or other frameworks.

Why is it not a good choice for those frameworks? The general purpose and challenges involved in transmitting data via JSON seem similar for Ember and mobile apps.

@BlakeWilliams
Copy link
Contributor

I don't have much experience from the iOS/Android side but was largely echoing what our iOS team has said in the past. As for other frameworks it doesn't seem worth the effort to parse JSON API yourself to build your models/objects when the "AMS Style" API's are a lot easier to work with.

@teoljungberg
Copy link
Contributor

Do we want to cover JSON schema in this PR?

@drapergeek
Copy link
Contributor

Why is it not a good choice for those frameworks? The general purpose and challenges involved in transmitting data via JSON seem similar for Ember and mobile apps.

This opinion was largely because of using a purely functional language to parse "side loaded" data.

I think another PR this makes sense.

@jferris
Copy link
Contributor

jferris commented Oct 21, 2015

This opinion was largely because of using a purely functional language to parse "side loaded" data.

https://github.com/thoughtbot/aeson-api/blob/master/test/Aeson/API/ParserSpec.hs

I think another PR this makes sense.

Yeah, agreed.

@drapergeek
Copy link
Contributor

@malandrina
Copy link
Author

Do we want to cover JSON schema in this PR?

@teoljungberg -- the HTTP API Design Guide recommends providing machine-readable JSON Schema schemata. Do you think additional guidelines pertaining to JSON Schema are needed here? If so, what do you have in mind?

@teoljungberg
Copy link
Contributor

@malandrina If that recommendation is documented, I'm happy.

@malandrina
Copy link
Author

@teoljungberg 👍 sounds good.

@mcmire
Copy link

mcmire commented Oct 23, 2015

I think these are all fine things. The recommendation to use oj makes sense, although it does mean that there would thereafter be a hard dependency on oj. What do you think about this?

@malandrina
Copy link
Author

I have slight misgivings about recommending a specific, non-thoughtbot gem for parsing JSON because it means we'll have to revisit the recommendation every so often and confirm that there isn't a better tool for the job. That said, there is precedent: We recommend TravisCI, SendGrid and a couple other non-thoughtbot tools/services in other sections of this file.

What do you think about changing the wording to be less specific? I'm thinking "Use a fast JSON parser, e.g. oj".

@jferris
Copy link
Contributor

jferris commented Oct 23, 2015

I agree about recommending a specific JSON parser.

Could this be better as a blog post? It feels like several of these recommendations could change over time, and a blog post would provide more opportunity to explain why we think they're a good idea right now. For example, you could discuss what we like about the Heroku guide or show before/after benchmarks for oj.

@malandrina
Copy link
Author

I think a blog post on this topic would be fantastic. However, I think it would also be nice to document our recommendations as they evolve. I view this addition to the guides as a starting point and forum for a long, ongoing conversation about how we should approach designing and building JSON APIs.

Would a Trello board or another thoughtbot repository be a better place to document these decisions and conversations?

@jferris
Copy link
Contributor

jferris commented Oct 23, 2015

However, I think it would also be nice to document our recommendations as they evolve.

Yeah, I think that's reasonable. I'm having a hard time thinking of things we could document that wouldn't depend on what the API was being used (authentication, versioning) for and wouldn't change quickly (oj).

Would a Trello board or another thoughtbot repository be a better place to document these decisions and conversations?

I think it makes sense to create Research cards for specific ideas we're trying out, but this seems like the best place to document our findings.

@malandrina
Copy link
Author

@jferris -- Cool. We're in agreement.

I made the JSON parser recommendation less specific in 4b3cd33.


[HTTP API Design Guide]: https://github.com/interagent/http-api-design
[oj]: https://github.com/ohler55/oj
[request specs]: https://www.relishapp.com/rspec/rspec-rails/docs/request-specs/request-spec
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're using an in-app JavaScript client as the primary consumer of the API, have we generally done this? Many of the applications I've seen rely on Capybara feature specs to make sure the controllers/requests are working.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the JSON Api is being consumed by something else "in process" then I agree. If your ultimate deliverable is the JSON API, then the request specs are the edge.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jferris, @derekprior -- great point. I agree. I modified this bullet point accordingly in 3e2c9db.

@malandrina
Copy link
Author

After reviewing our conversation here I feel confident that the concerns that have come up so far are addressed as of 3e2c9db. Does anyone want to share any additional thoughts or concerns before I merge this?

@jferris
Copy link
Contributor

jferris commented Nov 17, 2015

This generally looks good to me. I have to look through Heroku's API design guide some more. I wonder if we should specifically mention the parts from that guide that we endorse, since Heroku's ideas will evolve over time and won't necessarily mirror our own at all times.

@jessieay
Copy link
Contributor

I saw this post in Ruby Weekly and thought it might be relevant to this convo: http://multithreaded.stitchfix.com/blog/2015/11/04/simple-rails-apis-with-stitches/

@malandrina
Copy link
Author

@boddhisattva thanks for the feedback. I think you’re right that the phrase “in-app JavaScript client” is a bit cryptic. I’m hesitant to include a specific example because I’m trying to keep this section as concise as possible, but I did attempt to make the wording more descriptive in c7eb113.

@jferris good point — I think it’s a good idea to specify the sections of the Heroku API design guide we endorse. To me it looks like everything in the Responses, Requests and Artifacts sections is consistent with what we do (or aspire to do) now; I think Rails automatically does a lot of stuff advised in the Foundations section, but there are a couple things I’m not sure about, e.g. dividing large responses across requests with ranges.

@jessieay, thanks for the link! It’s cool to see how agreeing on a set of conventions gave StitchFix a foundation for building API-related tooling. stitches looks interesting; the functionality around formatting errors stands out as especially useful:

person = Person.create(params)
if person.valid?
  render json: { person: person }, status: 201
else
  render json: { errors: Stitches::Errors.from_active_record_object(person)
end

@bf4
Copy link

bf4 commented Nov 24, 2015

lurking ActiveModelSerializers collaborator here... we're definitely pro-JSON API and it's a goal for Rails. There's plenty of client and server libraries for JSON API. I personally want to get schema validation into ActiveModelSerializers.

I think the 'holy grail' of API development would be something like Document Driven Contracts

  1. Use a JSON Schema and validate against it. This may come from PRMD or jsonapi.org/schema or whatever. Heroku has a nice curl https://api.heroku.com/schema -H "Accept: application/vnd.heroku+json; version=3"
  2. Define each resource in terms of its JSON Schema representation (e.g. the gems swagger-core, json-schema-generator, json-schema)
  3. Generate client and server libraries from the defined resource e.g. https://github.com/interagent/heroics
  4. Generate interactive documentation from the resource schemas, such as PRMD ❤️ swagger
  5. Test against a server built from the schemas such as mountebank
  6. Validate requests against the chosen JSON Schema in a middleware
  7. Profit

I also like the https://github.com/cerebris/jsonapi-resources/ gem a lot.

@mcmire
Copy link

mcmire commented Nov 25, 2015

@bf4 If we as the Ruby community could come up with a tool that does all of those things in one bundle, and does it really well, that would be AMAZING.

One tool I've been wanting to try out that I saw a while back that might make it easier to do these kinds of things (specifically generating good documentation) was RAML.

@malandrina
Copy link
Author

@bf4 thanks for sharing your thoughts. I agree with @mcmire — if we could bundle all of that functionality together it would be incredible!

On a related note, but at the risk of derailing the broader conversation here about best practices, I have a post for the thoughtbot blog in review that covers the different JSON Schema-based tooling available in Ruby (the Heroku gems, pacto, json_matchers) and how they might all be used together. In the review process it became clear that there are compatibility issues (e.g. this one) between some of the different tools because they make different assumptions about how the user’s JSON Schema schemata are structured.

This experience makes me think that the main obstacle to developing an integrated suite of JSON Schema-based tools for Ruby web apps is ensuring that they all have compatible assumptions about the organization and structure of the schemata.

@mcmire, RAML looks cool! Not to mention WAY more user-friendly than JSON Schema. We should give it a try when the opportunity arises.

@mcmire
Copy link

mcmire commented Nov 25, 2015

This experience makes me think that the main obstacle to developing an integrated suite of JSON Schema-based tools for Ruby web apps is ensuring that they all have compatible assumptions about the organization and structure of the schemata.

Oh, definitely. While it's nice that we have separate tools that do separate things, and you can in theory use them on their own, they have to share that assumption.

Coming back to this PR, is there anything we need to add to merge it?

@malandrina
Copy link
Author

@mcmire, in #365 (comment) @jferris mentioned that he'd like to take a closer look at Heroku's API design guide. We may want to recommend specific sections of the guide rather than the whole thing. Otherwise there are no outstanding discussion items.

@toobulkeh
Copy link

I haven't followed all of the details in this conversation, but feel that two public efforts to solving this problem are probably worth noting (since I haven't seen reference to them yet):

  1. https://apiblueprint.org/ (the spec behind https://apiary.io/)
  2. http://swagger.io/

I hope these references help and provide some guidance.

@mcmire
Copy link

mcmire commented Dec 7, 2015

That's a good point.

I think that providing documentation for an API you're building is really important, and you should do it. Of course, there are different ways to do it.

I haven't tried any of the API generation tools out there except for prmd, although I have high standards when it comes to documentation:

  • It needs to be easy to go in as an author and add documentation. This pretty much means using Markdown.
  • Furthermore, I should be able to specify example request and responses easily, as well as possible errors that could occur for each endpoint, or perhaps generally across the API.
  • The documentation needs to be easy to read. If it lives on the web, this means no fancy JavaScript, just static pages essentially.
  • I don't want the documentation to look completely machine-generated. Mostly this means when describing request and response params, the documentation doesn't just give a table of "attribute name, required, type, description", although I guess at least that's better than nothing. But it also means giving me the option to add, say, a custom section with a bunch of free-form text, if I want to explain a certain concept.

Notable services that have quality documentation:

So those are my thoughts on generating documentation. I personally haven't used any of the API generation tools except for prmd, but any tool that we choose, I feel, needs to follow the guidelines above.

@boddhisattva
Copy link
Contributor

@malandrina Thanks for your detailed explanation. It really helped me to better understand where you are coming from based on the guidelines that you've written thus far wrt this PR.

When I just read the first sentence of the third guideline, it basically says Write integration tests for your API endpoints. The point immediately following it indicates when using feature specs or request specs make sense, so definitely I see there's a connection between the two sentences. But, as you've mentioned in your previous comment here that there are exceptions to this, what are your thoughts on specifying the very same point(about exceptions) in the guideline so that a user reading this guideline for the first time is also made aware that controller specs are still applicable in other cases.

@bf4
Copy link

bf4 commented Dec 11, 2015

@malandrina
Copy link
Author

@toobulkeh, @bf4 -- thanks for linking to all those resources! I know some folks at thoughtbot have used at least some of those standards and tools; my hope is that this section of the guides will help us formalize the adoption of the tools and workflows we find work best for us after we've taken them through our internal review process (a process we discuss in this episode of The Bike Shed podcast, if anyone is curious to learn more).

@boddhisattva, I see where you're coming from, but I think all of the best practices guidelines here are accompanied by an implicit directive to use your discretion, and I don't think it's necessary to make that directive explicit in this case.

@mcmire, just wanted to say I agree with all the points you made: You laid out an excellent framework for evaluating API documentation tools.

Pinging @jferris: Have you had a chance to look at Heroku's API design guide?

@jferris
Copy link
Contributor

jferris commented Dec 21, 2015

Looking through Heroku's API design guide, there aren't any points I really disagree with. Their design guide is fairly large, though, and it seems unlikely that we have group buy-in on every idea in there. There are several that I'm not sure I've seen at thoughtbot, such as 1.5 (setting Request-Id headers in responses) or 2.3 (using an /actions prefix for actions like "stop" instead of resources). Some of them conflict with other ideas we're trying; for example, 3.6 says to nest JSON resources, whereas JSON-API prefers references.

We may want to recommend reading the Heroku API deisgn guide when designing a new API.

If we've found some that are useful and near-universal from our own experience, we may want to reference them or replicate them in our own guide.

@w-vi
Copy link

w-vi commented Dec 22, 2015

@mcmire It looks like you have described API Blueprint where you get tools for rendering documentation for testing etc ... it is a shameless plug as I am actually working on those tools but I think they are worth checking as the philosophy and ideas behind it are very similar to what are your requirements.

@mcmire
Copy link

mcmire commented Dec 23, 2015

@w-vi Nice, thanks for sharing your tools!

@malandrina
Copy link
Author

@jferris sorry it's taken me a few weeks to address your feedback. Thanks for taking a look at the Heroku guide. The guide is indeed fairly large, and I agree that advising readers to adhere to all of its recommendations is too prescriptive at this point.

In my recent commit I changed the wording to:

Review the recommended practices outlined in Heroku's HTTP API Design Guide before designing a new API.

I think this wording is better because it points readers in the right direction but doesn't ask them to commit to a bunch of conventions and approaches we haven't evaluated yet.


* Review the recommended practices outlined in Heroku's [HTTP API Design Guide]
before designing a new API.
* Use a fast JSON parser, e.g. [`oj`][oj]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oj out of the box doesn't do exactly what you might expect:

irb(main):004:0> puts Oj.dump(foo: 'baz')
{":foo":"baz"}

I'd add a note here that for Ruby hashes w/ symbols you need to enable compat mode.

irb(main):005:0> Oj.default_options = {:mode => :compat }
=> {:mode=>:compat}
irb(main):006:0> puts Oj.dump(foo: 'baz')
{"foo":"baz"}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like for Rails 4.1~ there's a new gem that can be included:
https://github.com/ohler55/oj#compatibility

gem 'oj'
gem 'oj_mimic_json'

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@harlow good to know, thank you! I went ahead and merged this as-is but will see about expanding the recommendation to use oj to include some caveats about its usage in a separate PR.

@jferris
Copy link
Contributor

jferris commented Jan 11, 2016

All of the current proposed changes make sense to me.

@malandrina
Copy link
Author

Thanks @jferris! And thanks to everyone who contributed to this conversation!

Merged as 29f8443.

@bf4
Copy link

bf4 commented Apr 11, 2016

@malandrina wrote

I have a post for the thoughtbot blog in review that covers the different JSON Schema-based tooling available in Ruby

Did that get published? I haven't found it.

@malandrina
Copy link
Author

@bf4 nope, I kind of lost steam on that blog post because of the compatibility issues I encountered. It'd probably be worthwhile to revisit it though. I'll take a look this Friday.

@bf4
Copy link

bf4 commented Jul 5, 2016

@malandrina still no great resources on this for non json schema experts:(

@tysongach tysongach deleted the lw-add-json-api-guide branch January 23, 2017 15:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.