DLM Tutorials

Eventful React/Rails project

Part 1

RepoStarter CodeFinished Code
Rails API01_start01_end

If you’d like to code along with me here, clone down the repo to your machine and checkout the 01_start branch. Run bundle install and then rails db:create and you should be good to go.

For our initial setup, we’ll be following the Devise JWT Tutorial up to and including the Create User Model section. Later on in the series, we’ll complete the configuration to support JWT auth. For now, we just want to have the User model set up so we can set up its relationship with the Event model. If you want to clone down the GitHub repo and work along with me, you can checkout the 01_start branch and work along with me. Note, you’ll probably want to fork the repo to your own account first, that way you’ll be able to push if you want to. If you’re okay just coding along locally without pushing to GitHub, the code below will clone my repo directly to your machine.

git clone git@github.com:DakotaLMartinez/eventfull-api.git
git checkout 01_start

Then, you can compare that branch to the 01_end branch if you find anything going wrong.

git diff 01_end

Setting up CORS

To start, we want to set up a Cross Origin Resource Sharing (CORS) policy. We do this so that our rails API will be able to receive requests from origins. This is useful in the case where our API and our React code are managed separately and will be hosted on different domains when we deploy. To configure CORS, you’ll first want to find the line in the gemfile that refers to the rack-cors gem and uncomment that line.

gem 'rack-cors'

Then install it by running

bundle install

and finally, we’ll copy this into cors.rb configuration file.

# config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
    resource(
     '*',
     headers: :any,
     expose: ["Authorization"],
     methods: [:get, :patch, :put, :delete, :post, :options, :show]
    )
  end
end

We’re allowing full access to our api from all origins and to all of our resources. If we wan to, we can be more restrictive here. For example, if we intend to only allow requests to our API from our react application, we could put origins 'http://localhost:3000' in the configuration. We can also add a comma after that to specify where we’re hosting our application:

origins 'http://localhost:3000', 'https://myreactappdomain.com'

One notable downside to doing this at this point is that we’d be unable to use postman to send requests to our API without adding some additional configuration. So, we’re going to stick with origins '*' for now.

Adding additional dependencies

As our next step, let’s add in some gems. We’re only going to do surface level configuration for these now. We’ll come back and go into greater depth when we’re ready to use their features. For now let’s add these to the gemfile:

gem 'devise'
gem 'devise-jwt'
gem 'jsonapi-serializer'

This is a slight departure from the tutorial linked above. The reason for this is that Netflix has stopped maintaining the fast jsonapi gem and the JSON API Serializer is a maintained fork of that gem. To add these dependencies, run

bundle install

Now let’s get our initial devise configuration set up.

rails g devise:install

Next, find this line in devise.rb initializer:

# config.navigational_formats = ['*/*', :html]

and replace it with this:

config.navigational_formats = []

We do this so that devise won’t try to generate redirects after sign_in/sign_up. Also, let’s add the following line to config/environments/development.rb. This will be a placeholder for now as we may add the confirmable module later to allow confirming a user by their email.

config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

Create a Devise User

You can create a devise model to represent a user. It can be named as anything. So, I’m gonna be going ahead with User. Run the following command to create User model.

rails generate devise User

Then double check the migrations. Make sure you’ve got everything there that you want and then run:

rails db:create
rails db:migrate

Adding Groups

rails g scaffold Group name

For our app, we don’t need update and destroy for groups so we’ll remove the update and destroy actions from the GroupsController and update the routes.rb file like so:

resources :groups, except: [:update, :destroy]

We can test out our routes by running

rails routes | grep groups

We should get something like this:

 groups GET    /groups(.:format)          groups#index
        POST   /groups(.:format)          groups#create
  group GET    /groups/:id(.:format)      groups#show

Next, we can run the migration to add the table.

rails db:migrate

Setting up ActiveStorage to Support File Uploads

At this point, we’ll take a look at another of my tutorial about attaching file uploads to a model for use in a rails API only application.

We’ll start by adding active_storage migrations:

rails active_storage:install

You can read more about how these migrations support file uploads in this section of my tutorial. For now the important thing to note is that this will allow us to attach uploads to any model we like by adding a macro to it like this: has_one_attached :image or has_many_attached :images.

We can run rails db:migrate to add these tables to our database and enable support for file uploads.

Adding the Event Model

Now that we’ve got the ActiveStorage migration’s tables, we’re going to need a model we can attach uploads to. In our case, we’ll be creating an Event resource that can have a poster attached.

rails g scaffold Event name start_time:datetime end_time:datetime location

Now, we’ll need to make a couple of changes to allow uploading a poster. First, we’ll add a macro to the Event model:

class Event < ApplicationRecord
  has_one_attached :poster
end

Next, we’ll need to permit a poster through the params in the EventsController:

def event_params
  params.require(:event).permit(:name, :start_time, :end_time, :location, :poster)
end

Before we move on, let’s check the migration to create our events table. Add these two lines to the bottom

t.references :group, null: false, foreign_key: true
t.references :users, null: false, foreign_key: true

So we’ll have this for our migration:

class CreateEvents < ActiveRecord::Migration[6.1]
  def change
    create_table :events do |t|
      t.string :name
      t.datetime :start_time
      t.datetime :end_time
      t.string :location
      t.references :group, null: false, foreign_key: true
      t.references :users, null: false, foreign_key: true

      t.timestamps
    end
  end
end

now let’s run it

rails db:migrate

Now we can make a commit.

Add Relationships

Finally, we’re going to create our relationships.

class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :events
end
class Group < ApplicationRecord
  has_many :events
end
class Event < ApplicationRecord
  has_one_attached :poster
  belongs_to :user
  belongs_to :group
end

When we get started building out our React frontend, we’ll need to add in serializers as well so we can actually include related data in our json responses from the API. For now, this should be a good start. If you like, you can check out the resources below to get a sense of where we’re going.

Resources

PartStarter CodeEnding Code
1Starter CodeEnding code

Keep working in the woodshed until your skills catch up to your taste.
If you'd like to get in touch, reach out on LinkedIn.