masalibの日記

システム開発、運用と猫の写真ブログです

react graphqlをやってみた

今後のトレンドになるgraphqlでのWEBアプリを作ってみたかった。業務(仕事)でやらしてくれるはずがなかった。
諦めて自分でWEBアプリを作ることにした。

いきなりはできないので、すでに立っているgraphqlサーバーを利用する事にしました。

それはgithubです

5年前ぐらいに対応した・・・みたいな事を書いてあった。
またudemmyの学習でも利用しているのでそちらをベースにしました

謝罪

この記事は

https://maku.blog/p/qcp2cnx/

の丸パクリに近いです。
ほぼ自分用にメモとして残しています
違いについてはgraphqlのclientの接続部分は別ファイルにしたかったので別にしている

事前作業

プログラムを組む前に色々とやる事がありました

github tokenの作成

githubAPIにアクセスするにはtokenが必要です。

下記のページにアクセスして

https://github.com/settings/tokens
 → Generate new tokenを押してtokenを作成します

このtokenは基本的は公開しないものです。1回表示したあとに再度表示する事はできない。
scopeに関してはセキュリティを考慮して設定する感じです。

ApolloClientのインストール

graphqlサーバーにアクセスするためには3つの機能が必要です。

  • ApolloClient
  • ApolloProvider
  • InMemoryCache

一昔前だと1つ1つインストールする必要があったのですが、2020/11/03現在では下記の1つでOK

npm install @apollo/client'

プログラムについて

  • App.js(メインのプログラム)
  • client.js(graphqlのクライアントの設定などしているプログラム)
  • Issues.js(graphqlのクエリーを発行している部分)
  • .env.development.local(環境変数を設定しているプログラム)

App.js

import React from 'react';
import {ApolloProvider} from '@apollo/client';
import client from './client'
import {Issues} from './Issues';


function App() {
  return (
      <>
    <div className="App">
          hello,world
    </div>
    <ApolloProvider client={client}>
      <Issues />
    </ApolloProvider>
    </>    
  );
}

export default App;
  • ApolloProviderのタグが味噌かな。この範囲にある接続情報を設定している。
    ApolloクライアントをApolloProviderコンポーネントとReactに接続します。
    ApolloProviderはReactのに似ていContext.Providerます。Reactアプリをラップし、クライアントをコンテキストに配置します。これにより、コンポーネントツリーのどこからでもアクセスできるようになります
    https://www.apollographql.com/docs/react/get-started/
    公式のサンプルsourceは
import React from "react";
import { render } from "react-dom";
import {
  ApolloClient,
  InMemoryCache,
  ApolloProvider,
  useQuery,
  gql
} from "@apollo/client";

const client = new ApolloClient({
  uri: "https://48p1r2roz4.sse.codesandbox.io",
  cache: new InMemoryCache()
});

function ExchangeRates() {
  const { loading, error, data } = useQuery(gql`
    {
      rates(currency: "USD") {
        currency
        rate
      }
    }
  `);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return data.rates.map(({ currency, rate }) => (
    <div key={currency}>
      <p>
        {currency}: {rate}
      </p>
    </div>
  ));
}

function App() {
  return (
    <ApolloProvider client={client}>
      <div>
        <h2>My first Apollo app 🚀</h2>
        <ExchangeRates />
      </div>
    </ApolloProvider>
  );
}

render(<App />, document.getElementById("root"));

このsourceはわかりにくい・・・というか他に展開しにくい。

  • Issuesでクエリーを発行している
    今後はpropsでクエリーの内容を変えれるようにしたい

client.js

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

const GITHUB_TOKEN = process.env.REACT_APP_GITHUB_TOKEN
console.log(GITHUB_TOKEN)

// GraphQL クライアントを生成
const client = new ApolloClient({
  uri: 'https://api.github.com/graphql',
  headers: {authorization: `Bearer ${GITHUB_TOKEN}`},
  cache: new InMemoryCache(),
});

export default client;
  • uriの部分がgraphqlのエンドポイントになります。
  • headersでgithubのtokenを設定しています
  • 「cache: new InMemoryCache()」はお決まりみたいな感じで書いています。 下記のパターンの場合はoptionsを設定する
  • カスタム主キーフィールドを指定する
  • 個々のフィールドの保存と取得をカスタマイズします
  • フィールド引数の解釈をカスタマイズする
  • フラグメントマッチングのスーパータイプとサブタイプの関係を定義する
  • ページネーションのパターンを定義する
  • クライアント側のローカル状態を管理する

詳しくはこちら
https://www.apollographql.com/docs/react/caching/cache-configuration/

Issues.js

import * as React from 'react';
import {gql, useQuery} from '@apollo/client';

// 発行する GraphQL クエリ
const GET_ISSUES = gql`
  query {
    search(query: "repo:apollographql/apollo is:issue", type: ISSUE, first: 5) {
      issueCount
      nodes {
        ... on Issue { number title }
      }
    }
  }
`;

export const Issues: React.FC = () => {
  // GraphQL のクエリを実行
  const {loading, error, data} = useQuery(GET_ISSUES);

  // クエリ実行中の表示
  if (loading) return <p>Loading ...</p>;

  // エラー発生時(レスポンスがないとき)の表示
  if (error) return <p style={{color: 'red'}}>{error.message}</p>;

  // クエリの結果が返ってきたときの表示
  const {issueCount, nodes: issues} = data.search;
  return <>
    <h2>Num of issues: {issueCount}</h2>
    <ul>
      { issues.map(i => <li key={i.number}>{i.number} - {i.title}</li>) }
    </ul>
  </>;
};

loading, error, dataはお決まりみたい・・・。通常のjavascriptで組むと大変なんだような〜。本当に技術の革新に感謝。

.env.development.local

//(40バイトのランダム文字列)
REACT_APP_GITHUB_TOKEN=a1025xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

環境変数は必ず「REACT_APP_」を最初につけないといけない決まりがあるみたい

実際のsourceについて

たぶんcloneすればいける。

https://github.com/masalib/create-react-app-gitpod/tree/db2a13e4d949918cd6e034f70086c9b0a22f93a5