0
0

Next.js×Railsでデータを表示する。

Last updated at Posted at 2024-08-01

環境構築はこちらから
https://qiita.com/gabakugik/items/53dc8888f7085d15f4d8

今回Next.js(React)×Ruby on Railsを使いデータを表示させようと思います。

ツリー構造

|-- backend
|   |-- Dockerfile
|   |-- Gemfile
|   |-- Gemfile.lock
|   |-- README.md
|   |-- Rakefile
|   |-- app
|   |   |-- channels
|   |   |-- controllers
|   |   |-- jobs
|   |   |-- mailers
|   |   |-- models
|   |   `-- views
|   |-- bin
|   |   |-- bundle
|   |   |-- rails
|   |   |-- rake
|   |   `-- setup
|   |-- config
|   |   |-- application.rb
|   |   |-- boot.rb
|   |   |-- cable.yml
|   |   |-- credentials.yml.enc
|   |   |-- database.yml
|   |   |-- environment.rb
|   |   |-- environments
|   |   |-- initializers
|   |   |-- locales
|   |   |-- master.key
|   |   |-- puma.rb
|   |   |-- routes.rb
|   |   `-- storage.yml
|   |-- config.ru
|   |-- db
|   |   |-- migrate
|   |   |-- schema.rb
|   |   `-- seeds.rb
|   |-- lib
|   |   `-- tasks
|   |-- log
|   |   `-- development.log
|   |-- public
|   |   `-- robots.txt
|   |-- storage
|   |-- test
|   |   |-- channels
|   |   |-- controllers
|   |   |-- fixtures
|   |   |-- integration
|   |   |-- mailers
|   |   |-- models
|   |   `-- test_helper.rb
|   |-- tmp
|   |   |-- cache
|   |   |-- development_secret.txt
|   |   |-- pids
|   |   |-- restart.txt
|   |   |-- sockets
|   |   `-- storage
|   `-- vendor
|-- docker-compose.yml
`-- frontend
    |-- Dockerfile
    `-- app
        |-- README.md
        |-- app
        |-- components
        |-- next-env.d.ts
        |-- next.config.mjs
        |-- node_modules
        |-- package-lock.json
        |-- package.json
        |-- postcss.config.mjs
        |-- public
        |-- tailwind.config.ts
        |-- tsconfig.json
        |-- types
        `-- yarn.lock

まずはbackendでデータを作ります。

docker compose run backend bundle exec rails generate model Todo title:string content:text

docker compose run backend bundle install

docker compose run backend bundle exec rails db:migrate
rack-corsのインストール
gem 'rack-cors'のコメントアウトをはずす。
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins 'localhost:3001' # Next.jsを動作させているアドレスとポート番号

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
docker compose run backend bundle install
docker compose build
docker compose run backend bundle exec rails generate controller Todos
app/controllers/todos_controller.rb
class TodosController < ApplicationController
  # GET /todos
  def index
    # 日付が新しい順に10件まで取得する
    @todos = Todo.all.order(created_at: :desc).limit(10)

    render json: @todos
  end
end
config/routes.rb
Rails.application.routes.draw do
  resources :todos, except: [:new, :edit]
end
db/seeds.rb
Todo.create(title: 'Todo 1', content: 'Todo 1 の内容')
Todo.create(title: 'Todo 2', content: 'Todo 2 の内容')
Todo.create(title: 'Todo 3', content: 'Todo 3 の内容')
docker compose run backend bundle exec rails db:seed
docker-compose up -d
http://localhost:3000/todos
でデータが表示されるか確認

次はフロント部分を作っていきます

docker compose run --rm frontend npm install axios
docker compose run --rm frontend npm install swr

styles/globals.cssをすべて削除。

frontend/app/app/page.tsx
// トップページ
export default function Home() {
  return (
    <div className="flex flex-col justify-center items-center">
      トップページ
    </div>
  );
}

docker-compose up -d
でトップページと表示されればOK
frontend/types/Todo.ts
export type TodoType = {
  id: number;
  title: string;
  content: string;
};
frontend/components/Todo.tsx
import { TodoType } from '../types/Todo';

// Todo一つを表示するコンポーネント
const Todo = ({ todo }: { todo: TodoType }) => {
  return (
    <div className="focus:outline-none mb-7 bg-white p-6 shadow rounded">
      <div className="flex items-center border-b border-gray-200 pb-6">
        <div className="flex items-start justify-between w-full">
          <div className="pl-3">
            <p className="focus:outline-none text-lg font-medium leading-5 text-gray-800">
              {todo.title}
            </p>
          </div>
        </div>
      </div>
      <div className="px-2">
        <p className="focus:outline-none text-sm leading-5 py-4 text-gray-600">{todo.content}</p>
      </div>
    </div>
  );
};

export default Todo;
frontend/components/Todos.tsx
"use client";

import useSWR from 'swr';
import { TodoType } from '../types/Todo';
import Todo from './Todo';
import Link from 'next/link';

// Fetcher function to get data from the API
const fetcher = (url: string) => fetch(url).then(res => res.json());

const Todos = () => {
  // Use SWR to fetch and cache the Todo list
  const { data: todos, error } = useSWR<TodoType[]>('http://localhost:3000/todos', fetcher);

  if (error) return <div>Failed to load</div>;
  if (!todos) return <div>Loading...</div>;

  return (
    <div className="space-y-6 w-3/4 max-w-lg pt-10">
      <label className="block text-xl font-bold text-gray-700">Todo Index</label>
      <div className="items-center justify-center">
        {todos.map((todo) => (
          <Link
            href={`todos/${todo.id}`}
            key={todo.id}
          >
            <Todo todo={todo} />
          </Link>
        ))}
      </div>
    </div>
  );
};

export default Todos;


frontend/app/app/page.tsx
import Todos from '@/components/Todos';

// トップページ
export default function Home() {
  return (
    <div className="flex flex-col justify-center items-center">
      <Todos />
    </div>
  );
}

これで3件のデータが表示されます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0