Zero to API in 5 minutes
This quickstart will use Rails with ActiveRecord. Head to the guides section for usage with alternate ORMs or avoiding Rails completely.
If the below seems too “magical”, don’t worry - we’re just applying some sensible defaults to get started quickly.
Let’s start with a classic Rails blog. We’ll use a template to handle some of the boilerplate. Just run this command and accept all the defaults for now:
Feel free to run
git diff if you’re interested in the
particulars; this is mostly just installing gems and including modules.
Note: if a network issue prevents you from pointing to this URL directly, you can download the file and and run this command as
Resource defines how to query and persist your
Model. In other
Model is to the database as
Resource is to the API. So
first, let’s define our model:
Now we can use the built-in generator to define our
controller, and specs:
You’ll see a number of files created. If you open each one, you’ll see comments explaining what’s going on. Head over to the tutorial for a more in-depth understanding. For now, let’s focus on two key concepts you’ll see over and over again: inputs (via strong_resources), and outputs (via jsonapi-rb).
Our API Inputs are defined in
config/initializers/strong_resources.rb. You can think of these as
strong parameter templates.
Now run your app!:
http://localhost:3000/api/v1/posts renders JSON correctly.
Now we just need data.
We can seed data in two ways: the usual
db/seeds.rb, or using an HTTP
client. Using the client helps get your feet wet with client-side
development, or you can avoid the detour and plow right ahead.
db/seeds.rb to create a few
And run the script:
There are a variety of JSONAPI Clients out there. We’ll be using JSORM which is built to work with Suite-specific functionality like nested payloads. It can be used from the browser, but for now we’ll call it using a simple Node script.
Create the project:
Accept the default for all prompts. Now add the
JSORM dependency, as
well as a polyfill for
Add this seed code to
This should be pretty straightforward if you’re familiar with
ActiveRecord. We define
Model objects, putting configuration on
class attributes. We instatiating instances of those Models, and call
save() to persist. For more information, see the JSORM Documentation.
Run the script:
http://localhost:3000/api/v1/posts. You should have 3
Now that we’ve defined our
Resource and seeded some data, let’s see
what query functionality we have. We’ve listed all
http://localhost:3000/api/v1/posts. Let’s see what we can do:
- By title, ascending:
SELECT * FROM posts ORDER BY title ASC
- By title, descending:
SELECT * FROM posts ORDER BY title DESC
- By title, ascending:
- 2 Per page:
SELECT * FROM posts LIMIT 2
- 2 Per page, second page:
SELECT * FROM posts LIMIT 2 OFFSET 2
- 2 Per page:
- Sparse Fieldsets:
- Only render
SELECT * from posts(optimizing this query is on the roadmap)
- Only render
- Add one line of code:
SELECT * FROM posts WHERE title = "My title!"
- Any filter not whitelisted will raise
- All filter logic can be customized:
Customizations can be DRYed up and packaged into
- Sometimes you want to request additional fields not part of a normal response (perhaps they are computationally expensive).
- This can be done like so:
SELECT * FROM posts
You can conditionally eager load data or further customize this logic. See the tutorial for more.
- Useful for search grids - “Find me the first 10 active posts, and the total count of all posts”.
- One line of code to whitelist the stat:
SELECT count(*) from posts
- Combine with filters and the count will adjust accordingly.
- There are a number of built-in stats, you can also add your own.
This is rendered in the
metasection of the response:
- Error Handling:
- Your app will always render a JSONAPI-compliable error response.
- Cause an error:
View the default payload:
Different errors can be customized with different response codes, JSON, and side-effects. View jsonapi_errorable for more.
JSONAPI Suite supports full querying of relationships (“fetch me this
Post and 3 active
Comments sorted by creation date”), as well as
persistence (“save this
Post and 3
Comments in a single request”).
Let’s start by defining our model:
Configure the relationship in
- Whitelists the relationship.
- Knows to link the objects via
- Will use
CommentResourcefor querying logic (so we can say things like “only return the latest 3 active comments”)
- Uses an unfiltered base scope (
Comment.all). If we wanted, we could do things like
Comment.activehere to ensure only active comments are ever returned.
You should now be able to hit
/api/v1/comments with all the same
functionality as before. We just need to seed data.
Start by clearing out your database:
Again, you can seed your data using a NodeJS client or the traditional
Let’s edit our
node-seed/index.js. First add a
…and add the relationship to
Replace the existing
Post instances with one
Post and three
Tell our controller it’s OK to sidepost comments:
And tell our serializer it’s OK to render comments:
Now run the script to persist the
Post and its three
Comments in a
db/seeds.rb with this code to persist one
Now let’s fetch a
Post and filtered
Comments in a single request:
Any logic in
CommentResource is available to us. Let’s sort the
/api/v1/posts?include=comments&sort=-comments.created_at. This should work out-of-the-box.
Now add a filter to
That filter now works in two places:
This is why
Resource objects exist: they provide an interface to
functionality shared across many different endpoints, with no extra
We have a full CRUD API with robust querying functionality, and the
ability to combine relationships for both reads and writes. But what
happens when you need to customize the sorting logic? What about replacing
ActiveRecord with an alternate persistence layer, or avoiding Rails
These are important topics that JSONAPI Suite was built to address. To learn more about advanced usage and customization, we suggest following the tutorial. There are also a number of how-tos on this site, a good one to start with is How to Use without ActiveRecord
For additional documentation, view the YARD Docs.
For help with specific use cases, join our Slack chat!
Our generator applied some sensible defaults:
- Rspec Test runner
- jsonapi_spec_helpers Helpers to parse and assert on JSONAPI payloads.
- factory_girl for seeding our test database with fake data.
- faker for generating fake values, such as e-mail addresses, names, avatar URLs, etc.
- database_cleaner to ensure our fake data gets cleaned up between test runs.
By default we rescue exceptions and return a valid error response. In tests, this can be confusing - we probably want to raise errors in tests. So note our exception handling is disabled by default:
But you can enable it on a per-test basis:
In following this guide, we generated
Comment resources. Let’s edit our factories to seed randomized data:
Finally, we need to define a
Payloads use a
factory_girl-style DSL to define expected JSON. A
Payload compares a
Model instance and JSON output, ensuring:
- No unexpected keys
- No missing keys
- No unexpected value types
nullvalues (this is overrideable)
- Model attribute matches JSON attribute
- This can all be customized. See jsonapi_spec_helpers for more.
Let’s define our payloads now:
We can now run specs. Let’s start with the
You should see five specs, with one failing (
and one pending (
The reason for the failure is simple: our payload defined in
spec/payloads/post.rb specifies that a
Post JSON should include the
title. However, that spec is currently creating a
Post with no
attributes…which means in the response JSON,
values will fail
assert_payload unless elsewise configured.
So, let’s update our spec to POST attributes, not just an empty object:
Your specs should now pass. The only pending spec is due to a similar
issue - we need to specify attributes in
well. Follow the comments in that file to apply a similar change.
You should now have 5 passing request specs! These specs spin up a fake
server, then execute full-stack requests that hit the database and
return JSON. You’re asserting that JSON matches predefined payloads,
nulls or unknown key/values.
Go ahead and make the same changes to
Comment specs to get 10 passing
It’s up to you how far you’d like to go with testing. Should you add a
new spec to
spec/api/v1/posts/index_spec.rb every time you add a
allow_filter? This boils down to personal preference and
tolerance of failures. Try adding a few specs following the generated
patterns to get a feel for what’s right for you.
We can autodocument our code using swagger documentation. Documenting an endpoint is one line of code:
http://localhost:3000/api/docs to see the swagger documentation. Our custom UI will show all possible query parameters (including nested
relationships), as well as schemas for request/responses:
Our generator set up some boilerplate to enable this functionality, you can learn more at: How to Autodocument with Swagger