普段はApolloを使っているのですが、GQlessというものがあると聞き試してみました。 パッと見てめちゃくちゃ便利そうだったので、そのあたり紹介できればと思います。

コードはここに置いておきました。

https://github.com/yubachiri/play-gqless

特長

アプリケーションに書いたコードをもとに、よしなにクエリを発行してくれます。公式トップに書いてあるのはこれ。

https://gqless.com/

const App = () => {
  const { me, users } = useQuery();
  return (
    <div>
      Hello {me.name}!
      {users({ limit: 10 }).map((user) => (
        <User key={user.id || 0} user={user} />
      ))}
    </div>
  );
};

これを書くと、このqueryを投げてくれます。

query {
  me {
    name
  }
  users(limit: 10) {
    id
    name
  }
}

使うものだけきっちりとってきてくれるわけですね。
.graphqlファイルを自分で書くとなると、(まあないですが)そのページで使わないfieldまで取得してしまっている場合があります。前は使っていたけど今は使ってないものとか。

事前にschemaから型を自動生成させておくため、useQueryが何を返すのかもIDEに補完させることができます。
上記の例の「meがどんなフィールドを持っているのか」も定義されているため、アプリケーションを書いていて困ることはまあありません。

では、GQlessの使い方を見ていきましょう。

create-react-appして、GitHubのAPIを叩いて試してみることとします。

環境構築

create-react-appで、typescriptが使えるreactプロジェクトを作成します。

$ yarn create react-app gqless-sample --template typescript

プロジェクトのディレクトリで下記コマンドを実行し、パッケージを追加。

$ yarn add typescript @types/node @types/react @types/react-dom @types/jest

このあたり、詳しくはここを参照してください。
https://create-react-app.dev/docs/adding-typescript/

次にgqlessを入れます。

$ yarn add gqless
$ yarn add -D @gqless/cli graphql

したらpackage.jsonのscriptsにこれを追記。

"generate": "gqless generate"

こんな感じになります。

  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",
    "generate": "gqless generate"
  },

次に、型を自動生成させる設定をします。

あらかじめGitHubでトークン生成して、srcの下に .env.jsonファイルを作って書いておきます。

GitHub > Settings > Developer settings > Personal access tokens より、repoとuserにチェックを入れたものを作っておけばOKです。
ローカルで試すだけなら.env.jsonではなく該当箇所にベタ書きでも可です。その場合はうまいこと読みかえてください。

.env.json

{
  "GITHUB_TOKEN": "生成したトークン"
}
$ yarn generate

すると、gqless.config.cjs ができるのでこれを編集します。

introspqectionのendpointを、GitHubのAPIに変えます。 https://api.github.com/graphql
Authorizatoinヘッダも設定しましょう。

/**
 * @type {import("@gqless/cli").GqlessConfig}
 */
import { GITHUB_TOKEN } from './src/.env.json'

const config = {
  endpoint: "/api/graphql",
  enumsAsStrings: false,
  react: true,
  scalars: { DateTime: "string" },
  preImport: "",
  introspection: {
    endpoint: "https://api.github.com/graphql",
    headers: {
      Authorization: `bearer ${GITHUB_TOKEN}`
    },
  },
  destination: "./src/gqless/index.ts",
  subscriptions: false,
  javascriptOutput: false,
};

module.exports = config;

完了したらもう一度generateコマンドを実行します。

$ yarn generate

これによって型ファイルが作成されます。

ここまで完了したら、再びパッケージ追加です。

$ yarn add @gqless/react @gqless/subscriptions

以上で準備完了になります。

動かしてみる

App.tsxを編集して、yarn startします。

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useQuery } from './gqless';

function App() {
  const { viewer, $state } = useQuery()

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo"/>
        {$state.isLoading ? <p>loading</p> : (
          <p>{viewer.name}</p>
        )}
      </header>
    </div>
  );
}

export default App;

useQueryがGQlessの関数ですね。生成された型を見つつ、よしなにクエリを発行してくれるものです。
戻り値はqueryに定義されてるfieldと$stateです。$stateからはisLoadingが取れますね。

こんな画面になります。Reactロゴの下に自分の名前が表示されます。

Screen Shot 2021-04-02 at 11.11.26

また、サブリソースの取得はこんな感じになります。引数に取得条件を渡してあげると、そこを見てクエリを発行してくれます。

import React from 'react';
import logo from './logo.svg';
import './App.css';
import { useQuery } from './gqless';

function App() {
  const { viewer, $state } = useQuery()

  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo"/>
        {$state.isLoading ? <p>loading</p> : (
          <>
            <p>{viewer.name}</p>
            {viewer.followers({ first: 5 }).nodes?.map((follower, index) => {
              return <p key={follower?.id || index}>{follower?.name}</p>
            })}
          </>
        )}
      </header>
    </div>
  );
}

export default App;

クエリはこんな感じ。

query($first1:Int) {
  viewer {
    __typename 
    id 
    name 
    followers0: followers(first:$first1) {
      __typename 
      nodes {
        __typename 
        id
        name
      }
    }
  }
}

.graphqlファイルを書かなくても済む点、graphql-codegenと違い先に型を作ってくれる点がスマートな感じしますね。また、ページで使うフィールドのみを機械的に要求してくれるのも良きです。

まとめ

まだ確認していませんが、mutationやsubscription等も問題なく対応しているようです。また、キャッシュも備えているとのこと。
クエリの発行についてもuseQuery以外の選択肢があります。場面ごとに適したものを選べばより効率よくアプリケーションを実装することができそうです。
Apolloから乗り換えるかはわかりませんが、シンプルなアプリであれば工数が削減できかつエコな実装ができそうですね。

以上、GQlessの紹介でした。