Apollo-link-rest: `@ rest`ディレクティブはSSR環境では機能しません

作成日 2019年01月07日  ·  9コメント  ·  ソース: apollographql/apollo-link-rest

こんにちは!

開発/ブラウザで完全に機能する@rest graphqlクエリがありますが、サーバー(SSR)で同じコードを実行すると、長い不可解なエラーメッセージが表示されて失敗します。

@restディレクティブを無効にすると、ブラウザーとSSRの両方で、他のすべてが問題なく機能します)。

スタックトレース:

(node:26034) UnhandledPromiseRejectionWarning: Error: Network error: current.forEach is not a function
    at new ApolloError (.../demo-component/node_modules/src/errors/ApolloError.ts:56:5)
    at .../demo-component/node_modules/src/core/QueryManager.ts:512:31
    at .../demo-component/node_modules/src/core/QueryManager.ts:1046:11
    at Array.forEach (<anonymous>)
    at .../demo-component/node_modules/src/core/QueryManager.ts:1045:10
    at Map.forEach (<anonymous>)
    at QueryManager.broadcastQueries (.../demo-component/node_modules/src/core/QueryManager.ts:1039:18)
    at .../demo-component/node_modules/src/core/QueryManager.ts:411:18
(node:26034) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 5)
(node:26034) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

アドバイスを下さい :/

最も参考になるコメント

これをブートストラップファイルに追加すると、すべての問題が解決しました:D

if (global.Headers == null) {
    const fetch = require('node-fetch');
    global.Headers = fetch.Headers;
}

fetch-headers node-fetchを使用すると、問題が解決しました。

全てのコメント9件

もう少し掘り下げて、SSRなどを気にせずにNodeJSでクエリを実行しようとしています。

そしてこのコードを持っている:

            client
                .query({query: Site, variables: {domain: "demo.example.com"}})
                .then(
                    () => console.log("Fullfilled"),
                    ({graphQLErrors, networkError, message, extraInfo}) =>
                        console.error("Rejected", graphQLErrors, networkError, message,extraInfo)
                )

この結果で:

Rejected [] TypeError: current.forEach is not a function
    at /home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:671:13
    at Array.reduce (<anonymous>)
    at concatHeadersMergePolicy (/home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:664:23)
    at RestLink.request (/home/richard/Projects/skil/demo-component/node_modules/src/restLink.ts:1235:21)
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:76:19)
    at /home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:78:26
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/src/ApolloClient.ts:139:16)
    at ApolloLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:76:19)
    at /home/richard/Projects/skil/demo-component/node_modules/apollo-link/src/link.ts:78:26
    at DedupLink.request (/home/richard/Projects/skil/demo-component/node_modules/apollo-link-dedup/src/dedupLink.ts:39:30) Network error: current.forEach is not a function undefined

restLink.ts:671は、次のコードブロックです。

export const concatHeadersMergePolicy: RestLink.HeadersMergePolicy = (
  ...headerGroups: Headers[]
): Headers => {
  return headerGroups.reduce((accumulator, current) => {
    if (!current) {
      return accumulator;
    }
    if (!current.forEach) {
      current = normalizeHeaders(current);
    }
    current.forEach((value, key) => {    // <-- This line here
      accumulator.append(key, value);
    });

    return accumulator;
  }, new Headers());
};

https://github.com/apollographql/apollo-link-rest/blob/master/src/restLink.ts#L671

また、失敗したforEachを次のように置き換えた場合:

    Object.keys(current).forEach(key => {
      accumulator.append(key, current[key])
    })

すべてが再び機能します...

課題、私は上記の行の目的を理解していません:

    if (!current.forEach) {
      current = normalizeHeaders(current);
    }

これは常に失敗し、常にnormalizeHeaders()を実行するので?

ここで手足に出て、Nodeがnew Headers()の使用に腹を立てていると推測します。 新しいHeadersインスタンスには、そのプロトタイプにforEachメソッドがあります。 reduceに提供されたオブジェクトにforEachメソッドがない場合は、 normalizeHeadersを実行します。 これにより、変数がヘッダーのインスタンスであるかどうかが決まります。 そうでない場合は、 new Headers()を使用して新しいヘッダーのセットが生成されるため、ここで発生している問題が発生します。

Object.keys(current).forEach()の例は、プレーンオブジェクトを配列に正常に変換し、 .forEach()を呼び出すことができるため、機能しています。

次のステップのデバッグは賢明です... SSRアプリケーションでのnormalizeHeaders()の結果は何ですか? 出力内容を見てみると、Headersのインスタンスではなく、プレーンなオブジェクトであることがわかると思います。

私の知る限り、SSRが完全にテストされているとは思いませんが(#155を参照)、 @ fbarthoはおそらくこれにもっと光を当てることができます。

現時点では、SSRは直接テストまたはサポートされている機能ではありません。 理論的根拠:SSRを使用している場合は、独自のApollo GraphQLサーバーをデプロイでき、link-restは必要ありません。

しかし、一部の人々はそれを理解しています。 そして、私はそこでの世界の状態を改善するPRに反対していません。 SSRチュートリアルが必要なだけかもしれません。

さらに、ランタイム環境によっては、ヘッダーをポリフィルする必要があるのは比較的一般的です。 もうやってみましたか? @ Richard87

@tombartonの応答は正しいです。このコードの目的は、ヘッダーハッシュを適切なHeadersクラスに変換することです。

こんにちは、みんな!

私の問題に注意を払ってくれてありがとう;)

私のindex.jsの下部はこれです:

if (global.Headers == null) {
    global.Headers = require("fetch-headers");
}

// Set up babel to do its thing... env for the latest toys, react-app for CRA
// Notice three plugins: the first two allow us to use import rather than require, the third is for code splitting
// Polyfill is required for Babel 7, polyfill includes a custom regenerator runtime and core-js
require('@babel/polyfill');
require('@babel/register')({
    ignore: [/\/(build|node_modules)\//],
    presets: ['@babel/preset-env', '@babel/preset-react'],
    plugins: [
        '@babel/plugin-syntax-dynamic-import',
        '@babel/plugin-proposal-class-properties',
        '@babel/plugin-transform-instanceof',
    ]
});

// Now that the nonsense is over... load up the server entry point
require('./server');

だから私はヘッダーポリフィルを持っていると確信しています( global.Headersなしでも他の問題がたくさんあります)...

PHPアプリケーション(API-Platform + https://webonyx.github.io/graphql-php/)に緊密に統合されたGraphQLサーバーをすでに実行しているので、追加のApolloGraphQLサーバーをインストールしたくありません。 Reactアプリをより速くロードするには:D

restLink.tsのデバッグに苦労していますが、いくつかの戦略的なconsole.logを入力しましたが、 current _seems_は計画オブジェクトですが、 console.logは一部の情報を見逃している可能性があります..。。

編集...

export const concatHeadersMergePolicy: RestLink.HeadersMergePolicy = (
  ...headerGroups: Headers[]
): Headers => {
  return headerGroups.reduce((accumulator, current) => {
    if (!current) {
      return accumulator;
    }

    console.log(current.constructor.name)
    if (!current.forEach) {
      current = normalizeHeaders(current);
    }
    console.log(current.constructor.name)

    Object.keys(current).forEach(key => {
      accumulator.append(key, current[key])
    })

    return accumulator;
  }, new Headers());
};

「ヘッダー」を2回印刷します...

console.log(current.forEach)は、何があっても未定義で出力されます。ヘッダーポリフィルにforEachプロパティがないようです。

私のPHPStormはheadersにforEachメソッドがあると言っていますが、ノードでこのコードを実行するとheaders.forEach is not a functionを取得します:

const Headers = require("fetch-headers");
const headers = new Headers();

console.log(headers)
console.log(headers.forEach)
console.log(!headers.forEach)

headers.forEach(h => console.log(h))

出力:

node /home/richard/Projects/demo-component/src/test.js 
Headers {}
undefined
true
/home/richard/Projects/demo-component/src/test.js:10
headers.forEach(h => console.log(h))
        ^

TypeError: headers.forEach is not a function
    at Object.<anonymous> (/home/richard/Projects/demo-component/src/test.js:10:9)
    at Module._compile (internal/modules/cjs/loader.js:688:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:699:10)
    at Module.load (internal/modules/cjs/loader.js:598:32)
    at tryModuleLoad (internal/modules/cjs/loader.js:537:12)
    at Function.Module._load (internal/modules/cjs/loader.js:529:3)
    at Function.Module.runMain (internal/modules/cjs/loader.js:741:12)
    at startup (internal/bootstrap/node.js:285:19)
    at bootstrapNodeJSCore (internal/bootstrap/node.js:739:3)

これをブートストラップファイルに追加すると、すべての問題が解決しました:D

if (global.Headers == null) {
    const fetch = require('node-fetch');
    global.Headers = fetch.Headers;
}

fetch-headers node-fetchを使用すると、問題が解決しました。

このページは役に立ちましたか?
0 / 5 - 0 評価