Rails as an API

Spencer Smith
The Startup
Published in
11 min readJul 23, 2020

--

When first learning how to program, a lot of the projects you start with are already halfway finished. You might be learning from a Udemy course that gives you a default file structure, or maybe you’re enrolled with a Bootcamp that frequently starts you off with some pre-written code in an IDE. You’re comfortable working on a partially-competed project, but when you yourself have to create everything from scratch, you’re not always exactly sure how to light the fuse. This is a step-by-step guide for how to generally go about building a basic Rails API from scratch. For our demonstration, we’ll pretend that we’re building a basic Uber app where the relationships are as follows:

1. Rails New Appname

Move into the directory where you’d like your project to be located and run rails new <your-app-name-here> into the terminal.

This is going to give you a ton of structure that will allow you to get to the more customized parts of your project a little faster. In a nutshell, here are a few of the most important things that Rails is giving you “for free” when you enter this command. It’s good practice as a beginner to check these files out before doing anything else with your project.

config/routes.rb

This is where you will declare which routes will be available in your controllers. This should be empty to begin with, but routes will be added with future commands.

db/seeds.rb

Whenever you’re building the backend for a new app, it is essential that you populate your app with some “dummy data.” You’ll use this dummy data later on to ensure that your model relationships are working as expected, but for right now we can leave this file blank.

app/controllers, app/models, app/views

All three of these files will be blank to begin with, but it’s good to locate them before you need to use them. As we move forward, you’ll be checking these files to ensure that your program is being updated correctly. For right now, just make sure these folders exist.

2. Generate resources for non-join models

For our example, since Ride is the join model between Driver and Passenger, we’ll begin by working on the latter two. This means we will do all of step two for both Driver and Passenger before moving on to step 3.

While you may prefer to add your models, controllers, and views folders with separate commands, rails generate resource allows you to combine these steps into a single command. The structure is as follows:

rails generate resource ClassName first_attribute:integer second_attribute third_attribute:boolean

Notice that when declaring your attributes, any attribute that is a string may simply be declared without a colon. In this example, since the driver’s name will be a string, we do not need to include a colon followed by the data type.

Let’s take a look at what this command is going to do for us:

Thanks, Rails! With that single command, the above files have all been generated for us. While the test folders are important to be aware of, we’re just going to concern ourselves with the folders in yellow blocks for right now. Included in the yellow blocks are a migration, a model, a views folder, and access to the seven RESTful routes.

3. Generate resource for join model

We will be generating the exact same resources as we did for the non-join models, but we’ll declare the associations through “references.” The structure is as follows:

rails generate resource JoinClassName model_one:references model_two:references

Just as we did with the Driver and Passenger models, we get a migration, a model, a views folder, and access to the seven RESTful routes.

4. Check out file structure

After generating resources for all of your models (including join models), it’s now a great time to look at your file structure again to see if all of your files have been updated appropriately. Do one model at a time and ask yourself the following questions:

Did my routes update in config/routes.rb?

Were the migrations generated with the appropriate attributes and data types?

Do we have a model file under app/models?

Notice that the relationships have not yet been defined. We’ll address this in the next step.

Was my model controller added to app/controllers?

Do I have new views subfolders under app/views?

5. Add relationships to models

Do one model at a time. As you’re going from model to model, it’s still not a bad idea to cross-check your model relationships with your migrations to ensure that everything lines up the way you intend.

You’ll probably notice that in the instance of the join model, Rails has already filled in the relationships for us!

6. Migrate the database

After you’ve added your relationships to your models, most of your basic setup is complete. Our next goal should be populating the database with dummy data and using that data to test our models in the console, but to do this we have to first run our migrations.

If your migration was successful, you can now go inside of your schema file under db/schema.rb to check once again if your database will have the appropriate column names and data types.

7. Test model relationships and attributes in console

After we’ve migrated our database and checked our schema, we should now go inside of our rails console by entering rails c in the command line. After entering the console, we should check for at least the following four things:

Can I create new, temporary instances of my models?

After creating an instance, can I access its attributes?

Can I check an instance’s relationships to other models?

Do I have access to all instances of this class?

8. Create dummy data and seed the database

Now that we’ve tested that we can create new instances of our models, we may now “seed” our database with dummy data that we can use to further test our program. There are a number of ways you can do this, but one popular Ruby gem that can help is the Faker library.

First, we’ll need to add gem “faker” to our Gemfile located in the main directory.

Next, we’ll run bundle install in the terminal to install our new Faker gem

After we’ve calibrated our Gemfile by installing all of our gems, you can now use the seeds.rb file under db/seeds.rb. Use iteration and the .create method to generate some instances and fill your database with fake data.

After you’re happy with how your seed data looks, go ahead and attempt to seed your database in the console by running rails db:seed. It’s normal to have a couple of minor bugs (such as misspellings or missing parentheses) that need to be addressed before the database will accept your data.

9. Test dummy data in console

We will essentially be going back into the console exactly as we did in step 7, but now we’ll be able to more easily test our relationships and see if the seed file populated our database as we intended.

Can I access all instances of this class?

Can I access the attributes and relationships for a specific instance?

10. Add validations to models

Now that we’ve successfully seeded our database, it would be smart to make sure we can’t accidentally create new instances of classes using the wrong data. For example, if we’re creating a new passenger, we’ll need to know their name as a string and age as an integer. But what if we completely screw up the data types?

Please don’t try this at home

Rails didn’t complain because we haven’t yet explicitly instructed the models to check for certain conditions before creating new instances. The integer 103 was converted to a string “103” and false converted to zero for age. This is where validations come in handy. Let’s add some conditions to the name and age attributes to prevent these contaminated instances from persisting.

There are plenty of options for validations you could add to your app, but for our example we’re just going to add the following validations:

  1. Passenger must pass in a name and it has to be a unique name
  2. Passenger must pass in an age

11. Test validations in console

After setting up validations in our models, let’s go back into our console and try to create “bad” instances. Notice how when our attempt at creating an instance doesn’t meet the validations, Rails won’t allow the instance to be persisted to the database. You can make sure the errors are what you expect them to be by checking instance.errors.full_messages

**IMPORTANT**: From here, you can do all of the following steps for as many of your controllers as you wish. But keep in mind that whichever controller corresponds to the data that you plan to use as your main API is probably where you should start.

12. Add RESTful route actions to controllers

While the standard reference to actions in Rails is CRUD, I actually like to think of the correct order being RCUD. The reasoning behind this will become more clear as you gain more experience, but ultimately it just makes more sense to be able to see (read) things before you’re able to create things.

Go through all of the models and your RESTful routes. You may not need all of them in each model and you can easily comment them out later, but they’re nice to have. Another thing to consider is that if you’re not using .erb files in your views folder, we can leave out the “new” and “edit” routes.

If you wind up completing the back end of your project and realize there are certain routes that you never needed, you can comment out the routes and adjust the config/routes.rb file later.

13. Add Strong Params as a private method

Strong params can be viewed as a filter to ensure that your data is updated or created appropriately and they are represented as a method beneath a “private” keyword near the bottom of your file. These will be necessary to handle our “create” and “update” routes.

14. Add variables to routes

For our routes to have access to the data they will be responsible for handling, we need to provide them with variables containing appropriate data.

Since our INDEX route will be responsible for displaying all instances of our data, we can simply give it a variable containing all of the instances.

Our SHOW route is responsible for displaying just one instance of a class, so for now all we need to do is to find whichever particular instance we’re looking for. We can do this by finding the instance by their ID, which is unique and stored in our params hash.

To CREATE a new instance when the program passes through the create route, we need access to multiple attributes for any given instance. Let’s make use of our newly-implemented strong params.

If we want to UPDATE an instance, we break our process down into two steps: first we find the particular instance we’re looking for, then we update it.

Lastly, DELETE-ing an instance also requires two steps: find the instance, then destroy the instance.

You might have noticed that the first thing we do for several of our routes is find a specific instance of a class. If you’d like to reduce your code by a few lines, you can use Rails’ built-in before_action method. This method will allow you to automatically trigger a given method before any number of routes that you specify. This isn’t totally necessary, but it can help a lot with readability as your program grows larger. Refer to the picture below:

15. Render JSON

Our API is just about done on the back-end, but if we want our changes to be passed back to the front-end and rendered appropriately, we’ll need to call render json at the bottoms of our routes.

16. Application Controller inherits from ActiveRecord::API

You may have noticed that all of our controllers inherit from another controller: ApplicationController. The default behavior of this controller is to inherit from ActiveRecord::Base, so we simply need to change it to inherit from ActiveRecord::API

17. Houston, we have a server!

Congrats! Now all we have to do to get this bad boy up and running is to go into our terminal and enter rails server or rails s for short.

To view our new, functional API, we simply need to go to localhost:3000/api-controller-name-here in the browser

--

--