React + Graphqlで快適スキーマ駆動開発
React+Graphqlの開発体験がすごくいいので、みんなに体験してもらいたくて、手軽なチュートリアル作りました。
1. graphqlモックサーバーを立ち上げよう
-
create-react-appする
npx create-react-app {プロジェクト名} --template typescript
-
mockServer用のライブラリをインストール
yarn add apollo-server graphql graphql-tools
-
schema.graphqlを書いてみよう
type Book {
id: ID!
title: String!
author: Author!
}
type Author {
id: ID!
name: String!
age: Int
}
type Query {
books: [Book!]
}
- mockServer用のファイル
const { ApolloServer } = require("apollo-server");
const { loadSchemaSync } = require("@graphql-tools/load");
const { GraphQLFileLoader } = require("@graphql-tools/graphql-file-loader");
const schema = loadSchemaSync("mockServer/schema.graphql", {
loaders: [new GraphQLFileLoader()],
});
// サーバーを起動する
const server = new ApolloServer({ schema, mocks: true });
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
package.jsonのscriptに
"mock:start": "node mockServer/index.js"
追加して
yarn mock:start
サーバーが立ち上がって
localhost:4000で実際にクエリ試したりできるはず!!
2. graphql-codegenで型を生成
yarn add -D @graphql-codegen/cli
yarn graphql-codegen init
色々質問されるので答えてくと、codegenの設定ファイル勝手に作ってくれる
overwrite: true
schema: "mockServer/schema.graphql"
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
config:
withComponent: false
withHOC: false
withHooks: true
./graphql.schema.json:
plugins:
- "introspection"
package.jsonのscriptに
"generate": "graphql-codegen --config codegen.yml"
して
yarn generate
自動で型ファイルが生成されるはず!
3. graphql-codegenでhooksを作成
yarn add @apollo/client
src/components/BookCard/graphql/fragment.ts
import { gql } from "@apollo/client";
export const BOOK_CARD_FRAGMENT = gql`
fragment BookCard on Book {
id
title
author {
id
name
}
}
`;
src/pages/Book/List/graphql/queries.ts
import { gql } from "@apollo/client";
import { BOOK_CARD_FRAGMENT } from "../../../../components/BookCard/graphql/fragment";
export const BOOK_LIST_QUERIES = gql`
query GetBooks {
books {
...BookCard
}
}
${BOOK_CARD_FRAGMENT}
`;
overwrite: true
schema: "mockServer/schema.graphql"
documents: "src/**/graphql/*.ts" # 追加
generates:
src/generated/graphql.tsx:
plugins:
- "typescript"
- "typescript-operations"
- "typescript-react-apollo"
config:
withComponent: false
withHOC: false
withHooks: true
./graphql.schema.json:
plugins:
- "introspection"
再び
yarn generate
型ファイルが再度作り直されて
hooksが追加されるはず!!
4. 自動生成されたhooksの威力を体感しよう!
BookCardコンポーネントを作成
src/components/BookCard/index.tsx
import { FC } from "react";
import { BookCardFragment } from "../../generated/graphql";
import styles from "./style.module.css";
type Props = {
book: BookCardFragment;
};
const BookCard: FC<Props> = ({ book }) => {
return (
<div className={styles.card}>
<p>タイトル:{book.title}</p>
<p>著者:{book.author.name}</p>
</div>
);
};
export default BookCard;
BookListPageを作成
src/pages/Book/List/index.tsx
import { FC } from "react";
import BookCard from "../../../components/BookCard";
import { useGetBooksQuery } from "../../../generated/graphql";
import styles from "./style.module.css";
const BookListPage: FC = () => {
const { data, loading, error } = useGetBooksQuery();
if (loading) return <p>...loading</p>;
if (error) throw new Error(error.message);
if (!data?.books) return <p>データがありません。</p>;
return (
<div className={styles.page}>
<h1>本一覧</h1>
{data.books.map((book) => (
<div className={styles.cardWrapper} key={book.id}>
<BookCard book={book} />
</div>
))}
</div>
);
};
export default BookListPage;
src/App.tsx
import { FC } from "react";
import "./App.css";
import BookListPage from "./pages/Book/List";
const App: FC = () => {
return (
<div className="App">
<BookListPage />
</div>
);
};
export default App;
src/index.tsx
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
uri: "http://localhost:4000",
cache: new InMemoryCache(),
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById("root")
);
無事データが表示されたら終了!
お疲れ様でした!!
Discussion