使用Apollo Client开始GraphQL

这篇文章是YAMAP工程师Advent Calender 2021第5天的文章。

关于这篇文章

我写了这篇文章,目的是为了共享在前端GraphQL引入过程中学到的知识和备忘录。

在这篇文章中,我们总结了使用Apollo Client将GraphQL引入到React应用程序的具体方法。

“Apollo Client是什么”

简单来说,这是一个很棒的库,用于在Web前端环境中处理GraphQL(主要为React,并通过集成支持其他框架)。它还具备缓存机制和状态管理机制,并具有现代化且非常易于理解的接口。

Apollo Client是一个全面的JavaScript状态管理库,可以帮助你通过GraphQL管理本地和远程数据。使用它可以获取、缓存和修改应用程序数据,同时自动更新UI。

安装

假设您已经建立好了 Next.js 应用程序,我们接下来将继续进行。

下方是 Nextjs 的介绍。

开发环境

我只需要中文的一个版本,以下是预期的开发环境。

    • エディタ

VSCode

言語

JavaScript または TypeScript

フレームワーク

Nextjs
React

安装图书馆

首先,安装与 Apollo Client 相关的库。

$ yarn add @apollo/client graphql

$ yarn add -D apollo

安装编辑器扩展功能

安装 VSCode 上的 Apollo GraphQL 扩展程序。

通过这个方法,将根据后面介绍的 GraphQL Schema,在编辑器上实现查询代码补全的功能(这将大大提高开发效率,毫无疑问地)。

创建Apollo的设置文件

接下来,在项目根目录中创建 apollo.cofig.js 文件。

module.exports = {
  client: {
    includes: ['./src/**/*.js'],
    service: {
      name: 'yamap-app',
      url: 'https://api.yamap.com/graphql',
    },
  },
};

在 client.service.url 中,我们写下了 GraphQL API 的终结点。

顺便提一下,还有一个叫Apollo Studio的服务,据说使用它可以变得幸福。如果正在使用它,只需指定已注册服务中图表的名称即可。

module.exports = {
  client: {
    includes: ['./src/**/*.js'],
    service: 'my-graph-in-apollo-studio'
  },
};

下载模式

使用事先安装好的 Apollo 包,下载模式。

$ yarn apollo service:download -c ./apollo.config.js graphql-schema.json

我认为 `graphql-schema.json` 文件是在项目根目录中生成的。这将使得编辑器(VSCode)上的代码补全功能能够生效。

实施

我将使用Apollo Client实际获取用户列表数据。

ApolloClient 的实现方式

我们要使用包含在 @apollo/client 中的 ApolloClient 类。

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

const client = new ApolloClient({
  uri: "https://api.yamap.com/graphql",
  cache: new InMemoryCache({}),
});

export default client;

将ApolloProvider集成进来。

如果你使用Next.js应用程序,需要在pages文件夹下创建_app.js文件,并实现自定义应用程序。

import { ApolloProvider } from "@apollo/client";
import client from "../apollo-client"; // さっき作った apolloClient を import

function CustomApp({ Component, pageProps }) {
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}

export default CustomApp;

获取数据(企业社会责任)

首先,使用 useFetch() 在客户端获取数据。

查询

import { gql } from "@apollo/client"

export const GET_USERS_QUERY = gql`
  query ListUsers($first: Int!, $after: String) {
    users(first: $first, after: $after) {
      pageInfo {
        endCursor
        hasNextPage
        hasPreviousPage
        startCursor
      }
      edges {
        cursor
        node {
          id
          databaseId
          nickname
        }
      }
    }
  }
`

客户端获取

import { useQuery } from "@apollo/client"
import { GET_USERS_QUERY } from "../users-query"

function Users () {
  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      first: 10,
    }
  });

  const users = data?.users.edges.map(edge => edge.node);

  if (loading) {
    return <div>Loading...</div>
  }

  if (error) {
    console.error(error);
    return null;
  }

  return (
    <div>
      <h1>ユーザー一覧</h1>
      <div>
        {users?.map(user => (
          <div key={user.id}>
            {user.nickname}
          </div>
        ))}
      </div>
    </div>
  )
}

export default Users;

获取数据(服务器渲染)

查询将重用之前提到的GET_USERS_QUERY。

服务器端获取

function Users ({ initialData, initialError }) {
  const { data, loading, error } = useQuery(QUERY, {
    variables: {
      ssr: false, // getServerSideProps によって、既にサーバーサイドでデータ取得済のため
      first: 10,
    }
  });

  if (!initialData && loading) {
    return <div>Loading...</div>
  }

  if (initialError || error) {
    console.error(error);
    return null;
  }

  const users = data?.users.edges.map(edge => edge.node) || initialData?.users.edges.map(edge => edge.node);

  return (
    <div>
      <h1>ユーザー一覧</h1>
      <div>
        {users?.map(user => (
          <div key={user.id}>
            {user.nickname}
          </div>
        ))}
      </div>
    </div>
  )
}

export async function getServerSideProps() {
  const { data, error } = await client.query({
    query: QUERY,
    variables: {
      first: 10,
    }
  });

  return {
    props: {
      initialData: data,
      initialError: error,
    },
 };

这就是全部了。

如果不考虑分页功能,SSR 的代码可能会更简单一些。

希望我的建议对你有所帮助。

bannerAds