🐳

Next.js + Ruby on Rails(APIモード) on Docker 構築手順

2021/04/11に公開2

Next.jsでフロントエンド、Ruby on RailsのAPIモードでバックエンドを構成するアプリケーションの開発環境をDocker上で構築する方法をまとめておきます。Railsで作成したAPIをフロントで表示する手順と、Next.jsをTypeScriptに対応させる方法も合わせて紹介します。

Dockerはインストールされている前提で説明を始めます。
今回用いたDockerのバージョンは以下の通りです。

docker version : 20.10.5
docker-compose version : 1.29.0

1. Dockerの準備

ファイル構成

以下の構成でファイルを作成します。

|--front/
|    |--Dockerfile
|--api/
|    |--Dockerfile
|    |--Gemfile
|    |--Gemfile.lock #空ファイル
|--docker-compose.yml

docker-compose.ymlの記述

docker-compose.yml
version: '3.7'

services: 
  db:
    image: mysql:5.7
    command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: sample
      MYSQL_PASSWORD: password
    ports:
      - 4306:3306
    volumes:
      - mysql-db:/var/lib/mysql
  api:
    tty: true
    depends_on:
      - db
    build:
      context: ./api/
      dockerfile: Dockerfile
    ports:
      - 3000:3000
    volumes:
      - ./api:/app
    command: rails server -b 0.0.0.0
  front: 
    build:
      context: ./front/
      dockerfile: Dockerfile
    volumes:
      - ./front/app:/usr/src/app
    command: 'yarn dev'
    ports:
      - "8000:3000"
volumes:
  mysql-db:
    driver: local

フロント側Dockerfileの記述

/front/Dockerfile
FROM node:14-alpine
WORKDIR /usr/src/app

サーバー側Dockerfileの記述

/api/Dockerfile
FROM ruby:2.7

ENV LANG=C.UTF-8 \
  TZ=Asia/Tokyo

WORKDIR /app
RUN apt-get update -qq && apt-get install -y nodejs default-mysql-client
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN bundle install

# Start the main process.
CMD ["rails", "server", "-b", "0.0.0.0"]

Gemfileの記述

/api/Gemfile
source 'https://rubygems.org'
gem 'rails', '~> 6.0.3', '>= 6.0.3.2'

上記ファイルが準備できたらビルドします。

$ docker-compose build

2. Next.jsアプリを作成する

yarn createでfrontディレクトリに作成します。

$ docker-compose run --rm front yarn create next-app .

アプリの生成が完了したら、起動してみます。

$ docker-compose up

localhost:8000にアクセスして、以下の画面が表示されていればNext.jsアプリの作成はOKです! 👏

3. Ruby on Railsでサーバー環境を構築する

続いて、RailsをAPIモードで立ち上げます。今回、データベースはMySQLを採用しました。

まず、apiディレクトリにRailsアプリを作成します。

$ docker-compose run --rm api bundle exec rails new . --api -d mysql

生成途中で、Gemfileのコンフリクトが生じますが、Yを入力して進めてください。

アプリケーションの作成が完了したら、database.ymlを変更します。
docker-compose.ymlに記述したデータベース設定に合わせてください。

database.yml
# MySQL. Versions 5.5.8 and up are supported.
・・・
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
+ password: password
- host: locaohost
+ host: db

development:
  <<: *default
- database: app_development
+ database: sample
・・・

Railsアプリを起動させます。

$ docker-compose up --build

localhost:3000にアクセスして、以下の画面が表示されていればRailsアプリの作成はOKです! 👏

4. JSONを返すAPIを作成する

Next.jsで用いるAPIをRailsで作成しましょう。

$ docker-compose run --rm api bundle exec rails g scaffold post title:string

各種ファイルが生成されます。

$ docker-compose run --rm api bundle exec rails db:migrate

postsテーブルが作成されます。
seedの機能を用いて雑に初期データを取り込みます。

seeds.rb
Post.create!(
  [
    {
      title: 'Next.js + Ruby on Rails + Docker の環境構築'
    },
    {
      title: 'React Hooks でカスタムフックを作る'
    },
    {
      title: 'GraphQL と Apollo Client 入門'
    },
    {
      title: '【TypeScript4.3】Template Literal Types'
    },
    {
      title: 'Tailwind CSS でダークモード実装'
    },
  ]
)

記述したテストデータをテーブルに取り込みます。

$ docker-compose run --rm api bundle exec rails db:seed

localhost:3000/postsにアクセスし、以下のようなJSONデータが確認できればOKです。

5. Next.jsでAPIから取得したデータを画面に表示する

Rails側の環境設定でホストを指定する

fetchする際に、localhost:3000ではNext.js側でエラーが発生するため、Rails側で設定を追加する必要があります。

development
Rails.application.configure do
・・・
+ # localhost:3000では通信に失敗するためhostをdocker-compose.ymlのコンテナ名に合わせる
+ config.hosts << "api"
end

index.jsでAPIを受け取り、表示する

では、いよいよNext.js側でデータを表示させます。

/front/app/pages/index.js
const Home = (props) => {
  return (
    <div>
      <h2>
          POSTの一覧
        </h2>
        <table>
          {props.posts.map((post) =>
            <tr>
              <td>{post.id}.</td>
              <td>{post.title}</td>
            </tr>
          )}
        </table>
    </div>
  )
}

export const getStaticProps = async () => {
  // URLはlocalhostではなくapiとなる
  const response = await fetch("http://api:3000/posts", {method: "GET"});
  const json = await response.json();

  return {
    props: {
      posts: json
    },
  };
}

export default Home;

docker-compose upでdockerを起動後、localhost:8000にアクセスし、以下のようにデータが表示されていれば完了です! 🥳

6. Next.jsアプリにTypeScriptを導入する

最後に、フロントエンド開発のベストプラクティスとしてデファクト化したTypeScriptの環境を構築して終了しようと思います。

インストール

必要パッケージをインストールします。

$ docker-compose run --rm front yarn add --dev typescript @types/node @types/react @types/react-dom

拡張子の変更

以下 js(jsx)ファイルを ts(tsx)ファイルに変更します。

  • ./pages/_app.js -> ./pages/_app.tsx
  • ./pages/index.js -> ./pages/index.tsx
  • ./pages/api/hello.js -> ./pages/api/hello.ts

各ファイルをTypeScript仕様に書き換える

./pages/api/hello.ts
import React from 'react'
import {AppProps} from 'next/app';
import '../styles/globals.css';

const MyApp = ({ Component, pageProps }: AppProps) => {
  return <Component {...pageProps} />
}

export default MyApp
./pages/index.tsx
import React, { FC } from "react";
import { GetStaticProps } from "next";

type Post = {
  id: number;
  title: string;
}

type Props = {
  posts: Post[];
}

const Home: FC<Props> = (props) => {
  return (
    <div>
      <h2>POSTの一覧</h2>
      <table>
	{props.posts.map((post) =>
	  <tr>
	    <td>{post.id}.</td>
	    <td>{post.title}</td>
	  </tr>
        )}
      </table>
    </div>
  )
}

export const getStaticProps: GetStaticProps = async context => {
  const response = await fetch("http://api:3000/posts", {method: "GET"});
  const json = await response.json();

  return {
    props: {
      posts: json
    },
  };
}

export default Home;

以上でTypeScriptでの開発環境が整いました。
ちなみに、package.jsonには以下の記述の記述が追加されています。

package.json
"devDependencies": {
    "@types/node": "^14.14.37",
    "@types/react": "^17.0.3",
    "@types/react-dom": "^17.0.3",
    "typescript": "^4.2.4"
  }

以上です!! 🥳
ぜひ参考にしてみてください。

Discussion

けんじけんじ

たくみさんの記事で無事railsで環境構築ができました!!
めちゃめちゃ助かりました!
超超ありがとうございました!!

たくみたくみ

ありがとうございます!!
お役に立ててとても嬉しいです 🥳