こんにちは!
開発/ブラウザで完全に機能する@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.
アドバイスを下さい :/
もう少し掘り下げて、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
を使用すると、問題が解決しました。
これは、apollo-link-restのドキュメントに追加する必要があります。 どのファイルにPRを上げる必要がありますか? https://github.com/apollographql/apollo-link-rest/blob/master/README.md、https://github.com/apollographql/apollo-link-rest/blob/master/docs/rest.mdまたはhttps://github.com/apollographql/apollo-link/blob/master/docs/source/links/rest.md ?
最も参考になるコメント
これをブートストラップファイルに追加すると、すべての問題が解決しました:D
fetch-headers
node-fetch
を使用すると、問題が解決しました。