59
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

apolloよりシンプルなGraphQL client, urqlの紹介

Last updated at Posted at 2020-05-27

データフェッチをgraphqlにする場合、clientライブラリとしてapllo-graphqlを選ぶことは多いと思います。しかし便利な機能を多く抱え込みすぎており、特にキャッシュ戦略は複雑でtoo muchな印象があります。そこで、よりシンプルでかつカスタマイズも柔軟にできるurqlを紹介します。

urqlとは

urqlは、JS及びReactのコンサル企業であるFormidableによって開発された、新しいgrqphql clientです。apolloをご存知の方は、react-apollo相当と思ってもらえればよいです。メインメンテナはstyled-componentsのメンテナでもあるPhil Plückthun。

下記のような特徴をもっています。

  • apolloより軽量なgraphql client
  • apolloよりシンプルなキャッシュ戦略
  • reduxやexpressのmiddlewareに似た"Exchange"による拡張性

公式ドキュメント: https://formidable.com/open-source/urql/docs/

streamを扱うライブラリであるwonka、及びそのラッパーであるreact-wonkaといった技術の上に作られています。よって、中身のコードはoperation(ネットワークリクエスト抽象)をstreamで扱うような構造になっており、そこにExchange(middlewareのようなもの)を注入することで拡張させることができるようになっています。

使い方

codesandboxで良いという人はこちら(国の一覧と詳細を持つ簡単なデモアプリです)
https://codesandbox.io/s/simple-urql-example-wz6zw

セットアップ

apolloよりも簡単にセットアップできます

import { Provider, createClient } from 'urql'

const client = createClient({
  url: 'http://localhost:4000/graphql',
})

const YourApp = () => (
  <Provider value={client}>
    {/* ... */}
  </Provider>
)

useQueryによる基本的なリクエスト

useQueryを使った基本的なリクエストの例です。option引数にパラメターを渡すことで振る舞いをカスタマイズすることができるようになっています。

import React from 'react'
import { useQuery } from 'urql'

const getTodos = `
  query GetTodos($limit: Int!) {
    todos(limit: $limit) {
      id
      text
      isDone
    }
  }
`

const TodoList = ({ limit = 10 }) => {
  const [result] = useQuery({
    query: getTodos,
    variables: { limit },
  });

  if (result.fetching) return 'Loading...'
  if (result.error) return <div>{result.error.message}</div>

  return (
    <ul>
      {result.data.todos.map(({ id, text }) => (
        <li key={id}>{text}</li>
      ))}
    </ul>
  )
}

Refetching (再リクエスト)

再リクエストは、第引数で得られるexecuteQueryで実現できます。

function() {
  const [res, executeQuery] = useQuery({
    query: getTodo,
    variables: { id }
  })

  return (
    <div>
      <div>{res.data.todo}</div>
      <button onClick={executeQuery}>refetch</button>
    </div>
  )
}

条件付きリクエスト

何らかの条件が揃うまでリクエストを送りたくない場合はpauseオプションを設定します。
例えばidが別の非同期処理の結果を待たなければいけないときに使えます。

  const [result] = useQuery({
    query: getTodo,
    variables: { id },
    pause: id
  });

キャッシュ

デフォルトのキャッシュの仕組み

urqlは"document" cacheの仕組みを持っています。すべてのquery + variablesの組み合わせはハッシュ化されレスポンスとともにキャッシュされるので、再度同じquery + variablesでリクエストが送られると、urqlはキャッシュされた結果を返します。ただし、同じ__typenameのmutationが送られたときはリソースが更新されたとみなされ、キャッシュはクリアされる仕組みになっています。

キャッシュをコントロールする - requestPolicy

urqlのQueryのキャッシュは、requestPolicyを設定することで自在に操ることができます。デフォルトの挙動は、cache-first(キャッシュがあればリクエストを送らずキャッシュデータを返す)です。

urqlのキャッシュ戦略(request policy)は4種類で、とてもシンプルです。例えばcache-and-networkは、キャッシュデータを返しながら、同時に最新をフェッチしに行く挙動をします。つまりユーザーはキャッシュがあればローディングを待たず即結果をみることができ、かつ少し遅れて最新のものを見ることができます。

useQuery({ query: q, requestPolicy: 'cache-and-network' });

4つのrequestPolicy

Policy 内容 使い所 
cache-first デフォルト。キャッシュがあれば必ずキャッシュを返す。 変更のすくないリソースへのクエリ
cache-only リクエストは投げず、常にキャッシュのみを返す SSR等、トリッキーな用途。通常は使わない
network-only キャッシュを使わず、常にリクエストを投げる 頻繁に変わるリソースへのクエリ
cache-and-network キャッシュがあればキャッシュを返し、その後リクエストを投げる 頻繁には変わらず、早く見せたいリソースへのクエリ

Exchange

urql特徴の一つが、Exchangeです。

urqlでは、リクエスト関数をoperationという単位で扱います。このoperation lifecycleは、リクエストが投げられてから返ってくるまでの「双方向」の流れをstream的に扱います。

例えば、リクエストとレスポンスを簡易的にコンソールに出力するようなExchangeはこのように書けます。

export const loggerExchange: Exchange = ({ forward }) => {
  if (!isDev) {
    return ops$ => forward(ops$)
  } else {
    return ops$ =>
      pipe(
        ops$,
        tap(op => {
          console.log(
            '[Exchange debug]: Incoming operation: ',
            op.query.definitions[0].name.value,
            `variables: ${JSON.stringify(op.variables)}`
          )
        }),
        forward,
        tap(result => {
          if (result.error) {
            console.log(
              `[Exchange debug]: Completed operation ${result.operation.query.definitions[0].name.value}`,
              result.error
            )
          } else {
            console.log(
              `[Exchange debug]: Completed operation ${result.operation.query.definitions[0].name.value}`,
              result.data
            )
          }
        })
      )
  }
}

その他の便利な機能

dev tool

Exchangesではコンソールにログを出力するサンプルを書きましたが、公式でリッチなデバッグツールも作られています。
https://formidable.com/open-source/urql/docs/advanced/debugging/

Graphcache

urqlのデフォルトのキャッシュ戦略は紹介したとおりですが、Graphcacheを使うことによりApollo ClientのようなNormalized Cacheを適用することもできます。

まとめ

今回紹介したurqlの他に、react-queryやSWRなど、ちょっとした群雄割拠状態となっているfetcherライブラリ界隈ですが、urqlはその中でもかなり特徴的な設計思想をもったライブラリです。

Graphqlの場合はApollo clientを使うケースが多いと思いますが、やや多機能すぎる嫌いがあります。特にアプリケーションがシンプルな場合はApolloの代替手段として、よりシンプルなurqlを検討してみてはいかがでしょうか。

59
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
26

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?