Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
20
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

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

データフェッチを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を検討してみてはいかがでしょうか。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
20
Help us understand the problem. What are the problem?