Apollo Client + React入門:TypeScriptでGraphQL CRUDを実装する
はじめに
この記事は「GraphQL入門:TypeScriptでスキーマ・Query・Mutationを実装する」の続きです。Apollo Serverで立ち上げたGraphQLサーバーに対し、React + Apollo ClientでCRUD操作を実装します。
セットアップ
Reactプロジェクトを作成する
npm create vite@latest graphql-client -- --template react-ts cd graphql-client npm install @apollo/client graphql npm run dev
Apollo Clientを設定する
// src/apollo.ts import { ApolloClient, InMemoryCache } from '@apollo/client' export const client = new ApolloClient({ uri: 'http://localhost:4000', cache: new InMemoryCache(), })
// src/main.tsx import { StrictMode } from 'react' import { createRoot } from 'react-dom/client' import { ApolloProvider } from '@apollo/client' import { client } from './apollo' import App from './App' createRoot(document.getElementById('root')!).render( <StrictMode> <ApolloProvider client={client}> <App /> </ApolloProvider> </StrictMode> )
ApolloProvider でアプリ全体をラップすることで、どのコンポーネントからでも useQuery / useMutation が使えます。
型定義
// src/types.ts export type User = { id: string name: string email: string }
Query(Read:データ取得)
useQuery でQueryを実行します。コンポーネントのマウント時に自動で実行されます。
// src/components/UserList.tsx import { gql, useQuery } from '@apollo/client' import type { User } from '../types' const GET_USERS = gql` query GetUsers { users { id name email } } ` type GetUsersData = { users: User[] } export function UserList() { const { loading, error, data } = useQuery<GetUsersData>(GET_USERS) if (loading) return <p>読み込み中...</p> if (error) return <p>エラー: {error.message}</p> return ( <ul> {data?.users.map(user => ( <li key={user.id}> {user.name}({user.email}) </li> ))} </ul> ) }
useQuery の返り値:
| プロパティ | 説明 |
|---|---|
loading |
取得中かどうか |
error |
エラー情報 |
data |
取得したデータ |
refetch |
再取得を手動でトリガー |
Mutation(Create:作成)
// src/components/CreateUser.tsx import { useState } from 'react' import { gql, useMutation } from '@apollo/client' const GET_USERS = gql` query GetUsers { users { id name email } } ` const CREATE_USER = gql` mutation CreateUser($name: String!, $email: String!) { createUser(name: $name, email: $email) { id name email } } ` export function CreateUser() { const [name, setName] = useState('') const [email, setEmail] = useState('') const [createUser, { loading }] = useMutation(CREATE_USER, { refetchQueries: [{ query: GET_USERS }], }) const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() await createUser({ variables: { name, email } }) setName('') setEmail('') } return ( <form onSubmit={handleSubmit}> <input value={name} onChange={e => setName(e.target.value)} placeholder="名前" /> <input value={email} onChange={e => setEmail(e.target.value)} placeholder="メール" /> <button type="submit" disabled={loading}>作成</button> </form> ) }
refetchQueries に GET_USERS を指定することで、作成後に一覧を自動再取得します。
Mutation(Update:更新)
// src/components/UpdateUser.tsx import { useState } from 'react' import { gql, useMutation } from '@apollo/client' const UPDATE_USER = gql` mutation UpdateUser($id: ID!, $name: String, $email: String) { updateUser(id: $id, name: $name, email: $email) { id name email } } ` export function UpdateUser({ id }: { id: string }) { const [name, setName] = useState('') const [updateUser] = useMutation(UPDATE_USER) return ( <span> <input value={name} onChange={e => setName(e.target.value)} placeholder="新しい名前" /> <button onClick={() => updateUser({ variables: { id, name } })}>更新</button> </span> ) }
Apollo InMemoryCacheは id フィールドをキーにキャッシュを管理します。更新系MutationでサーバーがIDつきのオブジェクトを返すと、キャッシュが自動で書き換わるため refetchQueries は不要です。
Mutation(Delete:削除)
// src/components/DeleteUser.tsx import { gql, useMutation } from '@apollo/client' const GET_USERS = gql` query GetUsers { users { id name email } } ` const DELETE_USER = gql` mutation DeleteUser($id: ID!) { deleteUser(id: $id) } ` export function DeleteUser({ id }: { id: string }) { const [deleteUser] = useMutation(DELETE_USER, { refetchQueries: [{ query: GET_USERS }], }) return ( <button onClick={() => deleteUser({ variables: { id } })}>削除</button> ) }
削除はキャッシュの自動更新が効かないため、refetchQueries で一覧を再取得します。
まとめ
| 操作 | Hook | キャッシュ更新 |
|---|---|---|
| 取得(Read) | useQuery |
マウント時に自動実行 |
| 作成(Create) | useMutation |
refetchQueries で再取得 |
| 更新(Update) | useMutation |
id 一致でキャッシュ自動更新 |
| 削除(Delete) | useMutation |
refetchQueries で再取得 |
ApolloProviderでアプリ全体をラップするとどのコンポーネントからでもHookが使えるuseMutationの第1戻り値が実行関数、第2戻り値が{ loading, error, data }- Apollo InMemoryCacheが
idをキーに管理するため、更新系はRefetch不要なことが多い - 削除はキャッシュから自動で消えないので
refetchQueriesかupdateで対応する
バックエンドの実装は「GraphQL入門:TypeScriptでスキーマ・Query・Mutationを実装する」を参照してください。