I am in the process of building out a JSON API for a registry application, that, when pinged by Salesforce, will fire off emails to prospective Flatiron students that notify them of their acceptance into the Flatiron School program. One thing that I noticed is that there aren’t many resources out there for setting up a testing environment and eventually building out a JSON API via Ruby on Rails. Hopefully this resource will shed a little more light on how to accomplish that.
The first step is to integrate a Rails serializer in order to encapsulate the JSON serialization of objects. We first install the
active_model_serializers gem into the Gemfile, and then bundle. Now, for each model that we want to serialize for JSON serialization, we need to create a serializer:
rails g serializer student.
Inside of the serializer file that has just been generated, we need to add in student attributes that will be defined and visible in the JSON API.
1 2 3
For example, this is how the JSON data will be represented on the API tree, based upon the order of attributes in the
student_serializer.rb file above:
1 2 3 4 5 6 7 8 9
There is a key called students, and inside of its value store, it holds a collection of student objects. Inside of the array collection, there is 1 student with an id of 4, first name of “Doctor”, last name of “Who”, and an email “firstname.lastname@example.org”. This is a demonstrative example of how the JSON object data will be rendered when the API is pinged.
app/controllers directory, I created a new folder system that allows for semantic versioning of the API. Currently my directory looks like this:
app/controllers/api/v1. Inside of that folder structure, I have one file:
students_controller.rb. We will come back to these files once we start building out our controller actions.
Next, I set up the routes for the
students resource. My
config/routes.rb file looks like this currently:
1 2 3 4 5
This is the basic setup for the API itself. At this point in the API development, we’re only concerned with the
create actions for the API Students Controller. Now, we’ll go ahead and set up the RSpec tests for the controller.
1 2 3 4 5 6 7 8 9 10 11 12
This describe block refers to the index action of the controller. In this block, we are testing for two expectations: for the JSON response for the index action to return a 200 status code, and for the JSON response to return the correct number of students that exist in the test database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
In order to get these tests passing, we first need to submit a get request for the
index action in a JSON format. We first expect the response to be successful, or more specifically, to result in a 200 status code.
We also need to test whether or not it returns all of the existing students in the test database. I’ve used the FactoryGirl gem to mock out the student list
create_list(:student, 5). I’ve also set up the
parsed_response variable, which will translate the response body in JSON format into a more readable format. Then I’ve set the expectation that the length of the
parsed_response[students] should be equal to 5, as specified in the
FactoryGirl.create_list(:student, 5) line.
1 2 3 4 5 6 7 8 9 10 11 12 13
This block refers to the show action. I am testing for three specific expectations: a successful JSON response to return a 200 status code, a successful response to return the correct student JSON object, and for the JSON response to return an error message for a student JSON object that doesn’t exist.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
We’ve built out a student mock using FactoryGirl, and will be using this to test whether or not the
show method returns the correct student from the test database based on the student’s ID. For each test, we are submitting a get request for the student we mocked out earlier, and we should expect a 200 response for the first test, and for
parsed_response['student'] to essentially be a valid student object returned from the test database. For the third test, we are asking to return a student with an ID of 10, which doesn’t exist in our database. We should expect an error messaged from
parsed_response['error'], and we should also expect the response to return a message saying that the object was not found.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
This block is describing the create method, which will take in an email address parameter. If the email address is valid, then it will fire off an email to that email address, and then fire off a success message within a JSON response. If the email address is invalid, then it won’t fire off the email, and will render an invalid message within a JSON response.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
In the first test, we’re passing in a valid email address inside of the post request for the
create action. If it’s valid, then we expect the JSON response to have a 200 status code, and we also expect
parsed_response to have a success message as well. The second test passes in an invalid email address. We expect the JSON response to return a bad request status, more specifically a 400 status code, as well as a invalid message inside of the
In order to make all of these tests pass, here’s how the corresponding
app/controllers/api/v1/students_controller.rb file looks:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
create methods should be pretty straightforward, but perhaps I should elaborate more on the private methods. Within the context of building an API, we only need to focus on two:
find_student method will query the Student model and its corresponding ActiveRecord database in order to find the student object with the ID attribute specified in params. In the event that it cannot find that corresponding student and Rails throws a
ActiveRecord::RecordNotFound error, then it will execute a rescue clause that will render a JSON response with two components: the message “Student does not exist” and a 404 status code (“Not Found”).
The ‘valid_email?(email_address)’ method is simply a regex that will parse a parameter passed in, and determine whether or not it is a valid email address. If it is valid, it will fire off an email in the
send_acceptance_email(email) method, but if it is not valid, then it will render a JSON response with an invalid format error message.