Apollo-link-rest: La directive `@rest` ne fonctionne pas sur l'environnement SSR

Créé le 7 janv. 2019  ·  9Commentaires  ·  Source: apollographql/apollo-link-rest

Salut!

J'ai une requête graphql @rest qui fonctionne parfaitement dans le développement/navigateur, mais lorsque j'exécute le même code sur le serveur (SSR), il échoue avec un long message d'erreur cryptique.

(si je désactive la directive @rest , tout le reste fonctionne parfaitement, à la fois dans le navigateur et dans SSR).

La trace de la pile :

(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.

Merci de me conseiller :/

Commentaire le plus utile

L'ajout de ceci dans mon fichier bootstrap a résolu tous mes problèmes :D

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

notez que l'utilisation node-fetch au lieu de fetch-headers a résolu le problème !

Tous les 9 commentaires

En creusant un peu plus, j'essaie juste d'exécuter la requête dans NodeJS, sans me soucier du SSR, etc.,

et avoir ce code :

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

Avec ce résultat :

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 est ce bloc de code :

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

De plus, si je remplace le forEach qui échoue par ceci :

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

Tout fonctionne à nouveau...

Le défi , je ne comprends pas le but des lignes ci-dessus :

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

Puisque cela échouerait toujours et exécuterait toujours normalizeHeaders() ?

Je vais sortir sur une branche ici et supposer que Node s'énerve à propos de l'utilisation de new Headers() . Une nouvelle instance Headers a une méthode forEach sur son prototype. S'il manque une méthode forEach à l'objet fourni à reduce, nous exécutons normalizeHeaders . Cela détermine si la variable est une instance de Headers. Sinon, il générera un nouvel ensemble d'en-têtes, encore une fois, en utilisant new Headers() , causant ainsi le problème que vous voyez ici.

Votre exemple Object.keys(current).forEach() fonctionne car il convertit avec succès un objet simple en un tableau, vous permettant d'appeler .forEach() dessus.

Prochaine étape de débogage... quel est le résultat de normalizeHeaders() dans votre application SSR ? Jetez un œil à ce qu'il produit et je suppose que vous constaterez qu'il s'agit d'un objet simple, plutôt que d'une instance d'en-têtes.

Autant que je sache, je ne pense pas que le SSR ait été entièrement testé (voir # 155) mais @fbartho peut probablement apporter plus de lumière à ce sujet.

Le SSR n'est pas une fonctionnalité directement testée ou prise en charge pour le moment. Justification : si vous avez SSR, vous pouvez déployer votre propre serveur Apollo GraphQL, et vous n'avez pas besoin de link-rest

Certaines personnes l'ont compris cependant. Et je ne m'oppose pas aux relations publiques qui améliorent l'état du monde là-bas. Il me semble que nous avons peut-être juste besoin d'un tutoriel SSR.

De plus, il est relativement courant d'avoir besoin de remplir les en-têtes en fonction de votre environnement d'exécution. Avez-vous déjà essayé cela? @Richard87

La réponse de @tombarton est correcte : le but de ce code est de convertir les hachages d'en-tête dans la classe Headers appropriée.

Salut tout le monde!

Merci pour l'attention portée à mon problème ;)

La partie inférieure de mon index.js est celle-ci :

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');

Je suis donc presque sûr d'avoir le polyfill d'en-tête (également sans le global.Headers , j'ai un tas d'autres problèmes) ...

J'ai déjà un serveur GraphQL en cours d'exécution étroitement intégré dans mon application PHP (API-Platform + https://webonyx.github.io/graphql-php/) Je ne veux donc pas installer un serveur Apollo GraphQL supplémentaire, je veux juste pour charger l'application React plus rapidement :D

J'ai du mal à déboguer restLink.ts, mais j'ai mis quelques console.log stratégiques mais ça current _semble_ être un objet de plan, mais console.log pourrait manquer des informations ...

Éditer...

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());
};

Imprime "En-tête" deux fois...

console.log(current.forEach) imprime Non défini quoi qu'il arrive, il semble que mon polyfill d'en-têtes n'ait pas de propriété forEach ?

Mon PHPStorm me dit que headers a une méthode forEach, mais lors de l'exécution de ce code dans le nœud, j'obtiens un 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))

Sortir:

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)

L'ajout de ceci dans mon fichier bootstrap a résolu tous mes problèmes :D

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

notez que l'utilisation node-fetch au lieu de fetch-headers a résolu le problème !

Cette page vous a été utile?
0 / 5 - 0 notes