インフラエンジニアがRubyでAPI作ってみた②

## 前置き

前回の記事に引き続き、今回もRubyでAPIを作成していきます。
Docker Desktop,SequelAce, Rubyがインストールされている方は同じように作ることができます。

https://qiita.com/KentaroYoshizumi/items/f8c6d002972b61e10001

参考記事:

https://qiita.com/mokio/items/7d36a7aa79a4bf5066e9

今回は前回よりも少しだけ難易度をあげて、
①ローカルにMySQLコンテナを作る。（アクセスできることを確認する）
↓
②ローカルでAPIを作る。(DBに値を登録する。)
↓
③コンテナ内のDBを参照してみる。

といった流れで進めようと思います。

## ①ローカルにMySQLコンテナを作ってアクセスしてみる。

まずは今回作成する一連のファイルを配置するためにディレクトリを切っていきます。

```
$ mkdir API_sample
```
作成したディレクトリに移動します。

```
$ cd API_sample
```

このディレクトリ直下にdocker-compose.ymlファイルを作成します。

```
$ touch docker-compose.yml
```

```docker-compose.yml
version: '3.8'
services:
  mysql:
    image: mysql:8.0
    volumes:
      - ./tmp/mysql:/var/lib/mysql
    ports:
      - 3306:3306
    environment:
      - MYSQL_ROOT_PASSWORD=********
```
version: '3.8'はdocker-compose.ymlの書式のバージョンを指定しています。
services:の部分に起動させるコンテナを記述します。
今回はDBのコンテナだけを起動するため、このような記述になっています。
tmp/mysqlディレクトリをコンテナ内の/var/lib/mysqlディレクトリとするように指定しています。(後述)
コンテナ内部にはMySQLの8.0verを作成し、3306ポートから接続しますという意味になります。
パスワードは今回生意気にもマスクしてみます。笑

またMySQLのデータを格納しておくためのディレクトリも作成しておきます。
```
$ mkdir tmp/mysql
```

下記のコマンドで、実際にMySQLに接続してみます。
```
$ mysql -h 127.0.0.1 -u root -p
```

![スクリーンショット 2022-04-24 1.45.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280830/fc77a115-d1c6-e343-470c-264f09b13c91.png)

無事MySQLに接続することができました。

![スクリーンショット 2022-04-24 14.56.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280830/257038a6-81e6-a802-b2d7-6b5b7b4a7a75.png)

きちんとDBが作成されていることがわかります。


###### Gemfileとdatabase.ymlを書き換えて、rails⇨コンテナ内のMySQLに接続できるようにする。

デフォルトではsqlite3を利用する設定になっているため、MySQLを使うように設定しなおします。

```Gemfile
gem 'sqlite3', '~> 1.4' # この行を削除
gem 'mysql2' # この行を追加
```

config/database.ymlを書き換えます。

```database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  charset: utf8mb4
  collation: utf8mb4_bin
  host: <%= ENV['MYSQL_HOST'] || '127.0.0.1' %>
  port: <%= ENV['MYSQL_PORT'] || 3306 %>
  username: <%= ENV['MYSQL_USER'] || 'API_user' %>
  password: <%= ENV['MYSQL_PASSWORD'] || '*********' %>
  database: API_dev

development:
  <<: *default

test:
  <<: *default
  database: API_test

production:
  <<: *default
  database: API
```

これでrails⇨MySQLコンテナに接続できます。

dbにsetup_for_dev.sqlというファイルを作成して以下のように記述します。

```setup_for_dev.sql
CREATE USER IF NOT EXISTS API_user IDENTIFIED BY '********';

CREATE DATABASE IF NOT EXISTS API_dev DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

GRANT ALL PRIVILEGES on API_dev.* to API_user;

CREATE DATABASE IF NOT EXISTS API_test DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_bin;

GRANT ALL PRIVILEGES on API_test.* to API_user;
```

mysqlコマンドでファイルを読み込んで実行します。
```
$ mysql -h 127.0.0.1 -u root -p < ./db/setup_for_dev.sql
Enter password: 
```

MySQLにuserとDBを作成し,権限付与を実行することができました。


## ②ローカルにAPIを作る。（値を登録する。）
modelを作成します。

```
$ rails g model Category id:bigint name:string
```

参考記事にしたがってideaモデルを作成します。
```
$ rails g model Idea id:bigint category_id:bigint body:text
```

modelを作成したら、マイグレーションを実行します。下記のような文言が表示されたら成功しています。
```
$ rails db:migrate
```

```
$ rails db:migrate
== 20220424104105 CreateCategories: migrating =================================
-- create_table(:categories)
   -> 0.0536s
== 20220424104105 CreateCategories: migrated (0.0537s) ========================

== 20220424104517 CreateIdeas: migrating ======================================
-- create_table(:ideas)
   -> 0.0378s
== 20220424104517 CreateIdeas: migrated (0.0378s) =============================
```

modelの定義をしていきます。
```model/category.rb
class Category < ApplicationRecord
  has_many :ideas
end
```

```model/idea.rb
class Idea < ApplicationRecord
  belongs_to :category
end
```

次にコントローラーを作成していきます。

```
$ rails g controller categories
```

```
$ rails g controller ideas
```

続いてルーティングを書いていきます。
```config/routes.rb
Rails.application.routes.draw do
  resources :categories, only: [:index, :create]
  resources :ideas, only: [:index, :create]
end
```

次にコントローラーを作成します。

```categories_controller.rb
class CategoriesController < ApplicationController
  def index
    categories = Category.all
    render json: categories, each_serializer: CategorySerializer, include: [:ideas]
  end

  def create
    category = Category.new(category_params)
    if catgory.save
      render json: { status: 'SUCCESS', data: category }
    else
      render json: { status: 'ERROR', data: category.errors }
    end
  end

  private

  def category_params
    params.require(:category).permit(:name)
  end
end
```
一覧を取得し、新たに作成するアクションを定義します。
もう一つも作成し、

```ideas_controller.rb
class IdeasController < ApplicationController
  def index
    ideas = Idea.all
    render json: ideas, each_serializer: IdeaSerializer, include: [:category]
  end

  def create
    category = Category.new(category_params)
    idea = Idea.new(idea_params)
    if Category.exists?(name: params[:category][:name])
      idea_body.save
    else
      render json: ideas
    end
  end

  def idea_params
    params.require(:idea).permit(:body)
  end
end
```
としておきます。

Gemfileに以下を追加して　$ bundle install します。
```
gem 'active_model_serializers'
```

モデルを編集していきます。

```
class CategorySerializer < ActiveModel::Serializer
  attributes :id, :name, :created_at
  has_many :ideas
end
```

```
class IdeaSerializer < ActiveModel::Serializer
  attributes :id, :body
  belongs_to :category
end
```

Gemfileに以下を追加します。
```Gemfile
gem 'faker'
```

```
$ bundle install
```

```db/seeds.rb
  5.times do
  name = Faker::Construction.subcontract_category
  Category.create!(name: name)
end

10.times do
  category_id = rand(1..3)
  body = Faker::Food.dish
  Idea.create!(category_id: category_id, body: body)
end
```

無事データが格納されているか確認してみましょう。

## ③SequelAceでコンテナ内のDBを参照してみる。

SequelAceを起動します。
画像のような表示がされます。
![スクリーンショット 2022-04-24 23.03.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280830/d509a24b-f407-74a8-7cde-f5614312533d.png)

確認してみたところ、無事データが格納されていることが分かりました！！
![スクリーンショット 2022-04-24 23.01.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280830/86add104-488d-736e-7b8d-806dae105409.png)