Part 1 - Setting up Monorepo, APIs and frontend components.
Tags: rails, react, mongodb, ruby
Published: 2020-03-07 08:00:17

In this series, I will be covering on how to get started with Ruby on Rails, Mongo DB and React stack or I call them **R3M**. I will not bore you with a lot of explanation, So if you stuck at any point feel free to ask in comment. Being said that I assume you have a basic understanding of these languages and installed all the required software and tools. Okay, let's hop in. :)

*TLDR*: If you are bored to read step by step process and want to figure things by yourself kindly check my example repository.

*Github repository link*: https://github.com/koushikmohan1996/ror-react-mongo

# Monorepo

In this example, I will be using monorepo architecture. Monorepo will be very efficient for small apps and it is very easy to maintain. CI and CD will be very easy and we can make atomic commits in monorepo structure. You can read about Monorepo and other architecture online.

# Setup

Create 2 folders *server* and *client* before proceeding to next steps


### 1. RoR

Setting up and running an RoR app is very simple. They have an excellent document on how to do that. Since we are using react for our frontend we don't need *View* support from rails. Also, we will be using mongoid as ORM so we can avoid default ORM (active record) support from rails. Run the following command to create a Rails app.

```shell 
rails new server --api --skip-active-record
```

Add the below line in Gemlock file to add ORM support provided by mongo

```rb
gem 'mongoid', '~> 7.0.5'
```

Generate Mongo configuration file using the following command

```sh
rails g mongoid:config
```

Start rails server

```sh
rails s
```

### 2. React

Create a react app with the following command

```sh
npx create-react-app client
```

Now start the react app with npm/yarn command (according to your package manager)

```sh
yarn start (or) npm start
```

**Note:** Rails app may run in the same port of React. I this case React will automatically change its port.


# Backend API

To make everything simple, I will be building a simple notes app that can store title and content as a note.
*No authentication! No complex DB structure!*. If you need an example of authentication in Rails, I will cover it in a separate article.

As a first step, we should create a model to store notes details. Rails provide an easy way to generate these models using rails generator commands. You can learn them in detail from their official document.

```shell
rails g model Note title:String content:String
```

This command will create a model `notes.rb` in the models folder. You can check if the fields are correctly added to it.

Resources can be used for creating routes. It will support API architecture (get, post, put delete) by default. Add `resources :notes` to routes.rb file. You can now check the list of supported routes using command `rails routes`

Add a controller using the following generator command.

```shell
rails g controller notes
```

If you access `http://localhost:3000/notes` in browser or postman, it will throw an error saying that the action show is not defined in the NotesController. Let's go ahead and define it. Add the following line to notes_controller.rb

```rb
# GET /notes
  def index
    @notes = Note.all

    render json: @notes
  end
```

Similarly, you can add other methods for CRUD operation. You can also skip everything and use `rails generate scaffold Note title:string content:string` to generate Models, Controller, and routes. Refer notes_controller.rb in the example repository for all CRUD operations.

Since we are using React server and may host frontend in as separate service, we should add cors support. To do that add `gem 'rack-cors'` to Gemlock file and the below code to `application.rb`. You don't have to use a GEM for this but it provides a lot of customization which can be used later.

```rb
config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'
      resource '*', headers: :any, methods: [:get, :post, :options, :delete, :put]
  end
end
```

API server is almost ready now. You can check it by adding notes using any API tool such as Postman. If you check the API response you will see something like this:

```json
[
  {
    "_id":{
       "$oid":"5e622d49a394011924987ee3"
     },
    "content":"Sample notes",
    "title":"Test"
  }
]

```

Even though it is good enough, getting the `id` of the notes is tedious in frontend with this response. It will be much easier if we get the `id` directly as a string rather than an object with an additional layer. Add an `as_json` method in `models/note.rb` to override its default behavior. Now check the response again.

```rb
def as_json(*args)
  res = super
  res['id'] = self.id.to_s
  res
end
``` 

With this API server is ready and we can move forward to frontend. Yay!

# Frontend

I prefer React over other frontend frameworks. React highly flexible and small in size. But you may need additional packages like Router, Redux to build big applications. But I am not going to use these in this tutorial. Also, I will be using hooks based on components rather than class-based components. If you never worked on hooks you can check React's official document. 

There are many ways to organize files in your React app and this can change based on the size of your app. Create two folders `screens` and `service` in `client` and create a file `APIservice.js` under `service` folder.

```js
const API_URL = 'http://localhost:3000'

export const addNote = (title, content) => {
  return fetch(`${API_URL}/notes`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ title, content }),
  }).then(res => res.json());
};
```

You can add other methods similarly or you can copy it from the repo.

Instead of writing a single big component break your components into small pieces. It will be very easy to write and maintain many small components rather than a single big component. In our case, we will be splitting the MainPage.js into 3 components.

- A form to get inputs and submit it.
- A card component to display notes
- A container component to hold these components.

To make it simple am adding all the components in single file. But you can opt to create a components folder and maintain each component separately.

### Getting inputs from a form

```js
const NotesForm = (props) => {

  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [error, setError] = useState('');

  const handleSubmit = (evt) => {
    evt.preventDefault();
    addNote(title, content)
      .then(props.handleNoteAdded)
      .catch(e => setError('Error occurred while adding note'));
  };

  return (
    <div>
      <form style={{ display: 'inline-grid' }} onSubmit={handleSubmit}>
        <input type="text" placeholder="Title" className="input" onChange={e => setTitle(e.target.value)} value={title} />
        <textarea type="text" placeholder="Content" className="input" onChange={e => setContent(e.target.value)} value={content} />
        <input type="Submit" value="Add Notes" className="input" />
        <p className="error">
          {error}
        </p>
      </form>
    </div>
  )
};
```

### Card to display notes

```js
const NotesCard = (props) => {
  const { title, content, id } = props;

  const handleDelete = () => {
    deleteNote(id).then(props.onDelete);
  };

  return (
    <div className="card">
      <span><b>{title}</b></span>
      <p>{content}</p>

      <button onClick={handleDelete} className="card-button">Delete</button>
    </div>
  )
}
```

Now you can use these 2 components to compose a new component which will be our main page. Check out `MainPage.js` for the entire code.

```html
<div className="main-page-body">
  <NotesForm handleNoteAdded={getNotes}/>
  <div className="card-container">
    {
      notes.map(notesObj => <NotesCard
        content={notesObj.content}
        title={notesObj.title}
        id={notesObj.id}
        onDelete={getNotes}
      />)
    }
  </div>
</div>
```


# Motivation 

With this a basic version of Notes app ready. I am new to Ruby on Rails and am not an expert in React too and that is the motivation to start this series. This series will have tutorials, information which I wish I would see in a single place to kick start my project. In the next article, I will cover about writing tests for React and Rails app. If you want anything, in particular, feel free to drop a comment.

*Happy to help* :)