前回は、Prisma を使って、ログイン機能を追加しました。

【GraphQL】Prismaを使って、ログイン機能を追加する
今回は、バックエンドで作成した GraphQL を、フロントエンドで接続します。
バックエンドのコードは、こちらです。
フロントエンドのテンプレートは、React と MUI で作成しました。
GraphQL を接続するために、React に GraphQL と Apollo Client をインストールします。
ターミナルで、npm install --save graphql @apollo/clientを実行しましょう。
GraphQL と Apollo Client がインストール完了した後、index.tsx に ApolloClient と ApolloProvider、InMemoryCache をインポートします。
ts
import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client";
ApolloClientで、バックエンドの URI と、キャッシュを設定します。
バックエンドの URI は http://localhost:4000/graphql、キャッシュは先程インポートした、InMemoryCacheを指定します。
tsx
const client = new ApolloClient({
uri: "http://localhost:4000/graphql",
cache: new InMemoryCache(),
});
url ではなく、uri なので、注意しましょう。
<App />をApolloProviderでラップします。
client を指定しなければいけないので、先程作成したclientを指定します。
tsx
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById("root")
);
これで、GraphQL を接続する準備が完了したので、実際にデータを取得してみます。
pages フォルダの Book.tsx を開きます。
Apollo Client から gql と useQuery をインポートします。
tsx
import { gql } from "@apollo/client";
gql で、取得したいクエリの雛形を作ります。
gql の中身は、バッククォートで囲みます。
tsx
const booksData = gql``;
バックエンドの graphql に移動して、books で一度データを取得してみましょう。

isRead が波線になっているので、バックエンドの schema.ts のtype BookにisReadを追加します。
ts
type Book {
id: Int!
title: String!
author: String!
createdAt: String!
category: Category!
isRead: Boolean!
}
では、GraphQL に戻って、allBooks ボタンをクリックすると、

本のデータを取得することができます。
今作成した、GraphQL をコピーして、フロントエンドのgql内に貼り付けます。
tsx
const booksData = gql`
query allBooks {
books {
id
title
author
category {
name
}
isRead
createdAt
}
}
`;
次に、useQueryをインポートします。
tsx
import { gql, useQuery } from "@apollo/client";
useQuery を使って、先程作成したbooksDataからdataを取り出します。
tsx
const { data } = useQuery(booksData);
アドレスは http://localhost:3000/book を指定して、console.log()で確認すると、

GraphQL のデータを取得できているのがわかります。
data を使って、books のデータをブラウザに表示させてみます。
data から books を取り出します。
tsx
const { books } = data;
books を map で展開します。
tsx
<TableBody>
{books.map((book) => (
<TableRow key={book.id}>
<TableCell>{book.id}</TableCell>
<TableCell>{book.title}</TableCell>
<TableCell>{book.author}</TableCell>
<TableCell>{book.category.name}</TableCell>
<TableCell>{book.isRead}</TableCell>
<TableCell>{book.createdAt}</TableCell>
</TableRow>
))}
</TableBody>
book の型を指定します。
tsx
type TypeBook = {
id: number;
title: string;
author: string;
category: {
name: string;
};
isRead: boolean;
createdAt: string;
};
tsx
<TableBody>
{books.map((book: TypeBook) => (
<TableRow key={book.id}>
<TableCell>{book.id}</TableCell>
<TableCell>{book.title}</TableCell>
<TableCell>{book.author}</TableCell>
<TableCell>{book.category.name}</TableCell>
<TableCell>{book.isRead}</TableCell>
<TableCell>{book.createdAt}</TableCell>
</TableRow>
))}
</TableBody>
一度、ブラウザで確認すると、

エラーが発生しました。
これは、GraphQL のデータを取得するより、React のレンダリングする方が早いので、エラーになりました。
エラーが発生しないように、データを取得するまでローディングします。
useQueryにloadingを追加します。
tsx
const { data, loading } = useQuery(booksData);
loading が発生しているときは、メッセージを表示するようにします。
tsx
if (loading) return <div>Loading...</div>;
では、サイド確認すると、


ローディングが発生した後、books のデータを表示することができました。
createDataは、必要ないので、削除します。
今のところ、読了欄には、何も表示されていません。
isRead が true の場合『済』、false の場合『未』を表示します。
tsx
<TableBody>
{books.map((book: TypeBook) => (
<TableRow key={book.id}>
<TableCell>{book.id}</TableCell>
<TableCell>{book.title}</TableCell>
<TableCell>{book.author}</TableCell>
<TableCell>{book.category.name}</TableCell>
{book.isRead ? <TableCell>済</TableCell> : <TableCell>未</TableCell>}
<TableCell>{book.createdAt}</TableCell>
</TableRow>
))}
</TableBody>

無事、表示されました。
また、作成日も数字の羅列なので、修正します。
tsx
<TableBody>
{books.map((book: TypeBook) => (
<TableRow key={book.id}>
<TableCell>{book.id}</TableCell>
<TableCell>{book.title}</TableCell>
<TableCell>{book.author}</TableCell>
<TableCell>{book.category.name}</TableCell>
{book.isRead ? <TableCell>済</TableCell> : <TableCell>未</TableCell>}
<TableCell>
{`${new Date(Number(book.createdAt))}`
.split(" ")
.splice(1, 3)
.join(" ")}
</TableCell>
</TableRow>
))}
</TableBody>

GraphQL が誤っている場合、エラーメッセージを表示するようにします。
useQueryで、errorを取得します。
tsx
const { data, error, loading } = useQuery(booksData);
loadingと同様に、errorがある場合の表示を設定しましょう。
tsx
if (error) return <div>データを取得することができませんでした</div>;
試しに、gqlのidをiddにしてみると、

エラーメッセージを表示することができました。
全文は、以下の通りです。
フロントエンド:
バックエンド:
次回は、GraphQL で接続したデータ一覧から、詳細画面へ遷移します。

【GraphQL】フロントエンドで詳細画面へ遷移する