Nuxt Nation カンファレンスが開催されます。11月12日~13日にご参加ください。

graphql-request
nuxt-graphql-request

Nuxtと簡単に統合できる最小限のGraphQLクライアント

nuxt-graphql-request

📡 GraphQLリクエストモジュール

cinpm versionDependenciesnpm downloadscode style: prettierLicense: MIT

Nuxt.jsと簡単に統合できる最小限のGraphQLクライアントです。

機能

  • 最もシンプルで軽量なGraphQLクライアント。
  • PromiseベースのAPI(async / awaitで動作)。
  • TypeScript対応。
  • AST対応。
  • GraphQLローダー対応。

📖 リリースノート📄 ドキュメント

セットアップ

npx nuxi@latest module add graphql-request

Nuxt2の場合は、nuxt-graphql-request v6を使用してください


yarn add nuxt-graphql-request@v6 graphql --dev

nuxt.config.js

module.exports = {
  modules: ['nuxt-graphql-request'],

  build: {
    transpile: ['nuxt-graphql-request'],
  },

  graphql: {
    /**
     * An Object of your GraphQL clients
     */
    clients: {
      default: {
        /**
         * The client endpoint url
         */
        endpoint: 'https://swapi-graphql.netlify.com/.netlify/functions/index',
        /**
         * Per-client options overrides
         * See: https://github.com/prisma-labs/graphql-request#passing-more-options-to-fetch
         */
        options: {},
      },
      secondClient: {
        // ...client config
      },
      // ...your other clients
    },

    /**
     * Options
     * See: https://github.com/prisma-labs/graphql-request#passing-more-options-to-fetch
     */
    options: {
      method: 'get', // Default to `POST`
    },

    /**
     * Optional
     * default: false (this includes graphql-tag for node_modules folder)
     */
    includeNodeModules: true,
  },
};

ランタイム設定

ビルド時ではなく、実行時にエンドポイントを提供する必要がある場合は、ランタイム設定を使用して値を提供できます

nuxt.config.js

module.exports = {
  publicRuntimeConfig: {
    graphql: {
      clients: {
        default: {
          endpoint: '<client endpoint>',
        },
        secondClient: {
          endpoint: '<client endpoint>',
        },
        // ...more clients
      },
    },
  },
};

TypeScript

型定義はそのまま動作するはずです。TypeScriptは既にNuxtの自動生成された設定を拡張するように設定されているはずです。設定されていない場合は、ここから始められます

tsconfig.json
{
  "extends": "./.nuxt/tsconfig.json"
}

使用方法

コンポーネント

useAsyncData

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const query = gql`
  query planets {
    allPlanets {
      planets {
        id
        name
      }
    }
  }
`;

const { data: planets } = await useAsyncData('planets', async () => {
  const data = await $graphql.default.request(query);
  return data.allPlanets.planets;
});
</script>

ユーザー定義関数

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const query = gql`
  query planets {
    allPlanets {
      planets {
        id
        name
      }
    }
  }
`;

const planets = ref([])

const fetchPlanets = () => {
  const data = await $graphql.default.request(query);
  planets.value = data.allPlanets.planets;
}
</script>

ストアアクション

import { defineStore } from 'pinia';
import { gql } from 'nuxt-graphql-request/utils';
import { useNuxtApp } from '#imports';

type Planet = { id: number; name: string };

export const useMainStore = defineStore('main', {
  state: () => ({
    planets: null as Planet[] | null,
  }),
  actions: {
    async fetchAllPlanets() {
      const query = gql`
        query planets {
          allPlanets {
            planets {
              id
              name
            }
          }
        }
      `;

      const data = await useNuxtApp().$graphql.default.request(query);
      this.planets = data.allPlanets.planets;
    },
  },
});

GraphQLリクエストクライアント

公式graphql-requestライブラリの

HTTPヘッダーによる認証

nuxt.config.ts
export default defineNuxtConfig({
  graphql: {
    clients: {
      default: {
        endpoint: 'https://swapi-graphql.netlify.com/.netlify/functions/index',
        options: {
          headers: {
            authorization: 'Bearer MY_TOKEN',
          },
        },
      },
    },
  },
});
ヘッダーの増分設定

GraphQLClientの初期化後にヘッダーを設定する場合は、setHeader()またはsetHeaders()関数を使用できます。

const { $graphql } = useNuxtApp();

// Override all existing headers
$graphql.default.setHeaders({ authorization: 'Bearer MY_TOKEN' });

// Set a single header
$graphql.default.setHeader('authorization', 'Bearer MY_TOKEN');
エンドポイントの設定

GraphQLClientの初期化後にエンドポイントを変更する場合は、setEndpoint()関数を使用できます。

const { $graphql } = useNuxtApp();

$graphql.default.setEndpoint(newEndpoint);
リクエストごとにヘッダーを渡す

リクエストごとにカスタムヘッダーを渡すことができます。request()rawRequest()は、3番目のパラメーターとしてヘッダーオブジェクトを受け入れます

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const requestHeaders = {
  authorization: 'Bearer MY_TOKEN',
};

const planets = ref();

const fetchSomething = async () => {
  const query = gql`
    query planets {
      allPlanets {
        planets {
          id
          name
        }
      }
    }
  `;

  // Overrides the clients headers with the passed values
  const data = await $graphql.default.request(query, {}, requestHeaders);
  planets.value = data.allPlanets.planets;
};
</script>

fetchにさらにオプションを渡す

nuxt.config.ts
export default defineNuxtConfig({
  graphql: {
    clients: {
      default: {
        endpoint: 'https://swapi-graphql.netlify.com/.netlify/functions/index',
        options: {
          credentials: 'include',
          mode: 'cors',
        },
      },
    },
  },
});

または、setHeaders / setHeaderを使用する

const { $graphql } = useNuxtApp();

// Set a single header
$graphql.default.setHeader('credentials', 'include');
$graphql.default.setHeader('mode', 'cors');

// Override all existing headers
$graphql.default.setHeaders({
  credentials: 'include',
  mode: 'cors',
});

GraphQLドキュメント変数の使用

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const fetchSomething = async () => {
  const query = gql`
    query planets($first: Int) {
      allPlanets(first: $first) {
        planets {
          id
          name
        }
      }
    }
  `;

  const variables = { first: 10 };

  const planets = await this.$graphql.default.request(query, variables);
};
</script>

エラー処理

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const fetchSomething = async () => {
  const mutation = gql`
    mutation AddMovie($title: String!, $releaseDate: Int!) {
      insert_movies_one(object: { title: $title, releaseDate: $releaseDate }) {
        title
        releaseDate
      }
    }
  `;

  const variables = {
    title: 'Inception',
    releaseDate: 2010,
  };

  const data = await $graphql.default.request(mutation, variables);
};
</script>

GraphQLミューテーション

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const fetchSomething = async () => {
  const query = gql`
    {
      Movie(title: "Inception") {
        releaseDate
        actors {
          fullname # "Cannot query field 'fullname' on type 'Actor'. Did you mean 'name'?"
        }
      }
    }
  `;

  try {
    const data = await $graphql.default.request(query);
    console.log(JSON.stringify(data, undefined, 2));
  } catch (error) {
    console.error(JSON.stringify(error, undefined, 2));
    process.exit(1);
  }
};
</script>

生のレスポンスを受信する

requestメソッドは、レスポンスからdataまたはerrorsキーを返します。extensionsキーにアクセスする必要がある場合は、rawRequestメソッドを使用できます

import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const query = gql`
  query planets($first: Int) {
    allPlanets(first: $first) {
      planets {
        id
        name
      }
    }
  }
`;

const variables = { first: 10 };

const { data, errors, extensions, headers, status } = await $graphql.default.rawRequest(
  endpoint,
  query,
  variables
);
console.log(JSON.stringify({ data, errors, extensions, headers, status }, undefined, 2));

バッチクエリ

<script setup>
const { $graphql } = useNuxtApp();

const fetchSomething = async () => {
  const query1 = /* GraphQL */ `
    query ($id: ID!) {
      capsule(id: $id) {
        id
        landings
      }
    }
  `;

  const variables1 = {
    id: 'C105',
  };

  const query2 = /* GraphQL */ `
    {
      rockets(limit: 10) {
        active
      }
    }
  `;

  const query3 = /* GraphQL */ `
    query ($id: ID!) {
      core(id: $id) {
        id
        block
        original_launch
      }
    }
  `;

  const variables3 = {
    id: 'B1015',
  };

  try {
    const data = await $graphql.default.batchRequests([
      { document: query1, variables: variables1 },
      { document: query2 },
      { document: query3, variables: variables3 },
    ]);

    console.log(JSON.stringify(data, undefined, 2));
  } catch (error) {
    console.error(JSON.stringify(error, undefined, 2));
    process.exit(1);
  }
};
</script>

キャンセル

AbortControllerシグナルを使用してリクエストをキャンセルできます。

<script setup>
import { gql } from 'nuxt-graphql-request/utils';

const { $graphql } = useNuxtApp();

const fetchSomething = async () => {
  const query = gql`
    query planets {
      allPlanets {
        planets {
          id
          name
        }
      }
    }
  `;

  const abortController = new AbortController();

  const planets = await $graphql.default.request({
    document: query,
    signal: abortController.signal,
  });

  abortController.abort();
};
</script>

Node環境では、AbortControllerはバージョンv14.17.0以降でサポートされています。Node.js v12の場合は、abort-controllerポリフィルを使用できます。

import 'abort-controller/polyfill';

const abortController = new AbortController();

ミドルウェア

ミドルウェアを使用して、リクエストを前処理したり、生のレスポンスを処理したりできます。

リクエストとレスポンスのミドルウェアの例(各リクエストに実際の認証トークンを設定し、エラーが発生した場合にリクエストトレースIDを記録する)

function requestMiddleware(request: RequestInit) {
  const token = getToken();
  return {
    ...request,
    headers: { ...request.headers, 'x-auth-token': token },
  };
}

function responseMiddleware(response: Response<unknown>) {
  if (response.errors) {
    const traceId = response.headers.get('x-b3-traceid') || 'unknown';
    console.error(
      `[${traceId}] Request error:
        status ${response.status}
        details: ${response.errors}`
    );
  }
}

export default defineNuxtConfig({
  modules: ['nuxt-graphql-request'],

  graphql: {
    /**
     * An Object of your GraphQL clients
     */
    clients: {
      default: {
        /**
         * The client endpoint url
         */
        endpoint: 'https://swapi-graphql.netlify.com/.netlify/functions/index',
        /**
         * Per-client options overrides
         * See: https://github.com/prisma-labs/graphql-request#passing-more-options-to-fetch
         */
        options: {
          requestMiddleware: requestMiddleware,
          responseMiddleware: responseMiddleware,
        },
      },

      // ...your other clients
    },

    /**
     * Options
     * See: https://github.com/prisma-labs/graphql-request#passing-more-options-to-fetch
     */
    options: {
      method: 'get', // Default to `POST`
    },

    /**
     * Optional
     * default: false (this includes graphql-tag for node_modules folder)
     */
    includeNodeModules: true,
  },
});

FAQ

@nuxtjs/apolloではなく、なぜnuxt-graphql-requestを使用するのですか?

誤解しないでください。Apollo Clientは素晴らしいもので、vue / nuxtコミュニティによって適切にメンテナンスされています。graphql-requestに切り替える前に、18か月間Apollo Clientを使用していました。

しかし、私はパフォーマンスにこだわっているので、Apollo Clientは私にはまったく合いません

  • Vueエコシステムは十分なので、別の状態管理は必要ありません(Vuexと永続データ)。
  • データをフェッチするためにアプリでさらに約120kbを解析する必要はありません。
  • pusher.comを使用しているので、サブスクリプションは必要ありません。WSクライアントの代替手段もあります:http://github.com/lunchboxer/graphql-subscriptions-client

なぜgraphqlをインストールする必要があるのですか?

graphql-requestgraphqlパッケージのTypeScript型を使用しているため、TypeScriptを使用してプロジェクトをビルドしていて、graphql-requestを使用しているが、graphqlがインストールされていない場合、TypeScriptのビルドは失敗します。詳細はこちら。JSユーザーの場合は、技術的にはgraphqlをインストールする必要はありません。ただし、JSであってもTSタイプを認識するIDE(VSCodeなど)を使用している場合は、開発中に強化されたタイプセーフティの恩恵を受けることができるように、graphqlをインストールすることをお勧めします。

GraphQLドキュメントをgraphql-requestによってエクスポートされたgqlテンプレートでラップする必要がありますか?

いいえ。それは便宜上のもので、prettierフォーマットやIDE構文の強調表示などのツールサポートを得ることができます。何らかの理由で必要な場合は、graphql-taggqlを使用することもできます。

graphql-request、Apollo、Relayの違いは何ですか?

graphql-requestは、最もミニマルで使いやすいGraphQLクライアントです。小さなスクリプトやシンプルなアプリに最適です。

ApolloやRelayなどのGraphQLクライアントと比較して、graphql-requestには組み込みのキャッシュがなく、フロントエンドフレームワークとの統合もありません。目標は、パッケージとAPIをできる限り最小限に保つことです。

nuxt-graphql-requestはミューテーションをサポートしていますか?

もちろんです、以前と同様にGraphQLクエリとミューテーションを実行できます👍

開発

  1. このリポジトリのクローンを作成する
  2. yarn installまたはnpm installを使用して依存関係をインストールする
  3. yarn devまたはnpm run devを使用して開発サーバーを起動する

ロードマップ

📑 ライセンス

MITライセンス