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

今回は、バックエンドで作成したGraphQLを、フロントエンドで接続します。

バックエンドのコードは、こちらです。

フロントエンドのテンプレートは、ReactとMUIで作成しました。

GraphQLを接続するために、ReactにGraphQLとApollo Clientをインストールします。

ターミナルで、npm install --save graphql @apollo/clientを実行しましょう。

GraphQLとApollo Clientがインストール完了した後、index.tsxにApolloClientとApolloProvider、InMemoryCacheをインポートします。

import { ApolloClient, ApolloProvider, InMemoryCache } from "@apollo/client"

ApolloClientで、バックエンドのURIと、キャッシュを設定します。

バックエンドのURIはhttp://localhost:4000/graphql、キャッシュは先程インポートした、InMemoryCacheを指定します。

const client = new ApolloClient({
  uri: "http://localhost:4000/graphql",
  cache: new InMemoryCache()
})

urlではなく、uriなので、注意しましょう。

<App />ApolloProviderでラップします。

clientを指定しなければいけないので、先程作成したclientを指定します。

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <ApolloProvider client={client}>
        <App />
      </ApolloProvider>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

これで、GraphQLを接続する準備が完了したので、実際にデータを取得してみます。

pagesフォルダのBook.tsxを開きます。

Apollo ClientからgqlとuseQueryをインポートします。

import { gql } from "@apollo/client";

gqlで、取得したいクエリの雛形を作ります。

gqlの中身は、バッククォートで囲みます。

const booksData = gql`
`;

バックエンドのgraphqlに移動して、booksで一度データを取得してみましょう。

image2

isReadが波線になっているので、バックエンドのschema.tsのtype BookisReadを追加します。

type Book {
  id: Int!
  title: String!
  author: String!
  createdAt: String!
  category: Category!
  isRead: Boolean!
}

では、GraphQLに戻って、allBooksボタンをクリックすると、

image3

本のデータを取得することができます。

今作成した、GraphQLをコピーして、フロントエンドのgql内に貼り付けます。

const booksData = gql`
  query allBooks {
    books {
      id
      title
      author
      category {
        name
      }
      isRead
      createdAt
    }
  }
`;

次に、useQueryをインポートします。

import { gql, useQuery } from "@apollo/client";

useQueryを使って、先程作成したbooksDataからdataを取り出します。

const { data } = useQuery(booksData);

アドレスはhttp://localhost:3000/bookを指定して、console.log()で確認すると、

image4

GraphQLのデータを取得できているのがわかります。

dataを使って、booksのデータをブラウザに表示させてみます。

dataからbooksを取り出します。

const {books} = data;

booksをmapで展開します。

<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の型を指定します。

type TypeBook = {
  id: number;
  title: string;
  author: string;
  category: {
    name: string;
  };
  isRead: boolean;
  createdAt: string;
};
<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>

一度、ブラウザで確認すると、

image5

エラーが発生しました。

これは、GraphQLのデータを取得するより、Reactのレンダリングする方が早いので、エラーになりました。

エラーが発生しないように、データを取得するまでローディングします。

useQueryloadingを追加します。

const { data, loading } = useQuery(booksData);

loadingが発生しているときは、メッセージを表示するようにします。

if (loading) return <div>Loading...</div>;

では、サイド確認すると、

image6

image7

ローディングが発生した後、booksのデータを表示することができました。

createDataは、必要ないので、削除します。

今のところ、読了欄には、何も表示されていません。

isReadがtrueの場合『済』、falseの場合『未』を表示します。

<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>

image8

無事、表示されました。

また、作成日も数字の羅列なので、修正します。

<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>

image9

GraphQLが誤っている場合、エラーメッセージを表示するようにします。

useQueryで、errorを取得します。

const { data, error, loading } = useQuery(booksData);

loadingと同様に、errorがある場合の表示を設定しましょう。

if (error) return <div>データを取得することができませんでした</div>;

試しに、gqlididdにしてみると、

image10

エラーメッセージを表示することができました。

全文は、以下の通りです。

フロントエンド:

バックエンド:

次回は、GraphQLで接続したデータ一覧から、詳細画面へ遷移します。

ブログ一覧