Docker(-compose) + NextJS + RSC + GraphQL で作る
どうも
docker-composeなしではローカルでの開発ができない身体になってしまったおやまです。
さて本日は、前回の記事で作成したAPIを使ったサイトを構築するため、
フロントエンドの開発環境を構築し、JAMStackなサイトにするための設定(実装)に入っていきたいと思います
開発環境構築
プロジェクト用ディレクトリ,初期ファイルの作成
以下のような構成になるようにディレクトリを作成します
NextJS_Project
┣━ Dockrefile
┗━ docker-compose.yml
mkdir NextJS_Project
cd NextJS_Project
touch Dockerfile
touch docker-compose.yml
Dockerfile, docker-compose.ymlの中身は下記のように記述
Dockerfileの内容
FROM node:20.14.0
WORKDIR /app
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
のパッケージを利用します
docker-compose run app npm install graphql @apollo/client @apollo/experimental-nextjs-app-support @as-integrations/next
環境変数に接続情報を設定
前回の記事で発行されたAPIキー、シークレットキー、GraphQLのURIを定義しておきます
NEXT_PUBLIC_GRAPHQL_URI=https://{ドメイン}/graphql
NEXT_PUBLIC_API_KEY={APIキー}
NEXT_PUBLIC_SECRET_KEY={シークレットキー}
AppolloClientの実装
GraphQLアクセス時、リクエストヘッダーにAPIキー等認証情報が必要なので設定。
URIやAPIキーは前述の環境変数から読み込むように。
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
をインストールしてないので型はクエリに合わせて手書きです。
多分導入した方がグッと楽になるので状況に合わせて検討してくださいまし。
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
}
}
}
`;
次はユーザ表示のコンポーネントを実装します。
今回はテストなのでコンポーネントは適当です。
本題から外れるのでコンポーネントについての細かい話はしません、案件ではコンポーネント設計はしっかりやりましょう。しっかりやりましょう。(大事なことなので)
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ページに表示してみます
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についてお勉強させてもらった記事
RSCについてお勉強させてもらった記事
みんなすごい。めっちゃ勉強してる
おしまい
Discussion