🍣

Docker(-compose) + NextJS + RSC + GraphQL で作る

2024/06/05に公開

どうも
docker-composeなしではローカルでの開発ができない身体になってしまったおやまです。

さて本日は、前回の記事で作成したAPIを使ったサイトを構築するため、
フロントエンドの開発環境を構築し、JAMStackなサイトにするための設定(実装)に入っていきたいと思います

https://zenn.dev/shinokorori/articles/976652432775cd

開発環境構築

プロジェクト用ディレクトリ,初期ファイルの作成

以下のような構成になるようにディレクトリを作成します

NextJS_Project
 ┣━ Dockrefile
 ┗━ docker-compose.yml
mkdir NextJS_Project
cd NextJS_Project
touch Dockerfile
touch docker-compose.yml

Dockerfile, docker-compose.ymlの中身は下記のように記述

Dockerfileの内容

Dockerfile
FROM node:20.14.0

WORKDIR /app

docker-compose.ymlの内容

docker-compose.yml
services:
  app:
    build:
      context: .
    tty: true
    volumes:
      - ./src:/app
    environment:
      - WATCHPACK_POLLING=true
    command: sh -c "npm run dev"
    ports:
      - "3000:3000"

NextJSのインストール

docker-compose run --rm app sh -c 'npx create-next-app . --typescript'

コマンド発行後、初期設定をいろいろ聞かれます。
ある程度お好みですが、今回は下記のように選びました

✔ Would you like to use ESLint? … No / [Yes]
✔ Would you like to use Tailwind CSS? … [No] / Yes
✔ Would you like to use `src/` directory? … [No] / Yes
✔ Would you like to use App Router? (recommended) … No / [Yes]
✔ Would you like to customize the default import alias (@/*)? … [No] / Yes

完了したら一度起動して確認

docker-compose up -d
open http://localhost:3000

下記のような画面が表示されればインストール完了

現在のディレクトリ構造は下記のようになってるはずです

Apollo Client のセットアップ

必要なパッケージをインストール
AppRouterを使いつつ、後々JAMStackなサイト構成にするため、RSCでデータをフェッチする形にしたいので
@apollo/experimental-nextjs-app-supportのパッケージを利用します
https://www.npmjs.com/package/@apollo/experimental-nextjs-app-support

docker-compose run app npm install graphql @apollo/client @apollo/experimental-nextjs-app-support @as-integrations/next

環境変数に接続情報を設定

前回の記事で発行されたAPIキー、シークレットキー、GraphQLのURIを定義しておきます

src/.env.local
NEXT_PUBLIC_GRAPHQL_URI=https://{ドメイン}/graphql
NEXT_PUBLIC_API_KEY={APIキー}
NEXT_PUBLIC_SECRET_KEY={シークレットキー}

AppolloClientの実装

GraphQLアクセス時、リクエストヘッダーにAPIキー等認証情報が必要なので設定。
URIやAPIキーは前述の環境変数から読み込むように。

src/apollo/client.ts
import { ApolloClient, HttpLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { registerApolloClient } from "@apollo/experimental-nextjs-app-support";

const httpLink = new HttpLink({
  uri: process.env.NEXT_PUBLIC_GRAPHQL_URI,
});

const authLink = setContext((_, { headers }) => {
  const api_key = process.env.NEXT_PUBLIC_API_KEY;
  const secret_key = process.env.NEXT_PUBLIC_SECRET_KEY;

  return {
    headers: {
      ...headers,
      'zcc-api-key': api_key ? `${api_key}` : "",
      'zcc-secret-key': secret_key ? `${secret_key}` : "",
    },
  };
});

export const { getClient } = registerApolloClient(() => {
  return new ApolloClient({
    cache: new InMemoryCache(),
    link: authLink.concat(httpLink),
  });
});

Queryの実装

Query, Mutation の定義場所はいろいろありますが、表示箇所に応じて不要なカラムを取得したくないので、クエリはクエリで管理することにします
src/apollo/queries/
に配置することにして、ユーザリスト取得用のクエリを実装します。
今回はcodegenをインストールしてないので型はクエリに合わせて手書きです。
多分導入した方がグッと楽になるので状況に合わせて検討してくださいまし。

src/apollo/queries/getUserList.ts
import { gql } from "@apollo/client";

export type User = {
  id: string;
  name: string;
  xUrl: string;
  iconUrl: string;
  profile: string;
  tags: [Tag]
};

type Tag = {
  name: string;
}

export const GET_USER_LIST = gql`
  query GetUserList($per: Int, $page: Int, $tags: String) {
    userList(per: $per, page: $page, tags: $tags) {
      id
      name
      iconUrl
      profile
      xUrl
      githubUrl
      otherUrl
      tags {
        name
      }
    }
  }
`;

次はユーザ表示のコンポーネントを実装します。
今回はテストなのでコンポーネントは適当です。
本題から外れるのでコンポーネントについての細かい話はしません、案件ではコンポーネント設計はしっかりやりましょう。しっかりやりましょう。(大事なことなので)

src/components/user/allUser.tsx
import { getClient } from "@/apollo/client";
import { GET_USER_LIST, User } from '@/apollo/queries/getUserList';

export const GetAllUsers = async () => {
  const result = await getClient().query({
    query: GET_USER_LIST,
    variables: { per: 100, $page: 1, $tags: "" }, 
  });

  return (
    <div>
      <ul>
        {result.data.userList.map((user: User, idx: number) => (
          <li key={String(idx)}>
            {user.name} ({user.tags[0].name})
          </li>
        ))}
      </ul>
    </div>
  );
};

作成したコンポーネントをrootページに表示してみます

src/page.tsx
import styles from "./page.module.css";
import { GetAllUsers } from "./components/user/allUser";

export default function Home() {
  return (
    <main className={styles.main}>
      <GetAllUsers />
    </main>
  );
}

ここまで実装できたら一度 localhost:3000 にアクセスしてみます

なんとも殺風景ですが、前回作成登録したAPIから情報が取れました。

やったねタエちゃん!

あとは実装あるのみ!

これで一通りクソめんどくさくてやる気が出ない環境構築とAPIとの連携までいったん終わりです
このあとは一番楽しいゴリゴリ実装フェーズ。がんばりまっしょい。

あ。もっとだるい本番インフラとかあったけど、今は忘れておこう

おまけ

AppRouterについてお勉強させてもらった記事
https://zenn.dev/yamadadayo123/articles/6cb4f586de0183

RSCについてお勉強させてもらった記事
https://zenn.dev/sc30gsw/articles/0941e76ae96260

みんなすごい。めっちゃ勉強してる

おしまい

https://www.zizo.ne.jp/

Discussion