Apollo-link-rest: A diretiva `@rest` não funciona no ambiente SSR

Criado em 7 jan. 2019  ·  9Comentários  ·  Fonte: apollographql/apollo-link-rest

Oi!

Eu tenho uma consulta @rest graphql que funciona perfeitamente em desenvolvimento/navegador, mas quando executo o mesmo código no servidor (SSR), ele falha com uma longa mensagem de erro enigmática.

(se eu desabilitar a diretiva @rest , todo o resto funciona perfeitamente, tanto no navegador quanto no SSR).

O rastreamento de pilha:

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

Conselho por favor :/

Comentários muito úteis

Adicionar isso no meu arquivo bootstrap resolveu todos os meus problemas :D

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

aviso usando node-fetch em vez de fetch-headers resolveu o problema!

Todos 9 comentários

Um pouco mais de escavação, estou apenas tentando executar a consulta no NodeJS, sem me preocupar com SSR etc,

e tem este código:

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

Com este resultado:

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 é este bloco de código:

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

Além disso, se eu substituir o forEach que falha com isso:

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

Tudo volta a funcionar...

O desafio , eu não entendo o propósito das linhas acima:

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

Como isso sempre falharia e sempre executaria normalizeHeaders() ?

Vou arriscar aqui e acho que o Node está ficando chateado com o uso de new Headers() . Uma nova instância de Headers tem um método forEach em seu protótipo. Se o objeto fornecido para a redução estiver faltando um método forEach, executamos normalizeHeaders . Isso determina se a variável é uma instância de Headers. Caso contrário, ele gerará um novo conjunto de cabeçalhos, novamente, usando new Headers() , causando o problema que você está vendo aqui.

Seu exemplo Object.keys(current).forEach() está funcionando porque está convertendo com sucesso um objeto simples em um array, permitindo que você chame .forEach() nele.

Próximo passo depuração sábio... qual é o resultado de normalizeHeaders() em seu aplicativo SSR? Dê uma olhada no que ele produz e acho que você descobrirá que é um objeto simples, em vez de uma instância de Headers.

Tanto quanto sei, não acredito que o SSR tenha sido totalmente testado (consulte # 155), mas @fbartho provavelmente pode esclarecer mais sobre isso.

O SSR não é um recurso testado ou suportado diretamente no momento. Justificativa: se você tiver SSR, poderá implantar seu próprio servidor Apollo GraphQL e não precisará de descanso de link

Algumas pessoas descobriram isso no entanto. E não estou me opondo a relações públicas que melhorem a situação do mundo lá. Parece-me que talvez precisemos apenas de um tutorial SSR.

Além disso, é relativamente comum precisar preencher cabeçalhos com polyfill dependendo do seu ambiente de tempo de execução. Você já tentou isso? @Richard87

A resposta do @tombarton está correta: o objetivo desse código é converter hashes de cabeçalho na classe Headers apropriada.

Olá a todos!

Obrigado pela atenção ao meu problema ;)

A parte inferior do meu index.js é esta:

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

Então, tenho certeza de que tenho o polyfill de cabeçalho (também sem o global.Headers , tenho vários outros problemas) ...

Eu já tenho um servidor GraphQL rodando fortemente integrado no meu aplicativo PHP (API-Platform + https://webonyx.github.io/graphql-php/) Então eu não quero instalar um Apollo GraphQL Server extra, eu só quero para carregar o aplicativo React mais rápido :D

Tenho dificuldade em depurar restLink.ts, mas coloquei alguns console.log estratégicos, mas current _parece_ ser um objeto de plano, mas console.log pode perder algumas informações ...

Editar...

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 "Cabeçalho" duas vezes...

console.log(current.forEach) imprime Indefinido, não importa o que aconteça, parece que meu polyfill de cabeçalhos não tem uma propriedade forEach ?

Meu PHPStorm me diz que headers tem um método forEach, mas ao executar este código no nó recebo um 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))

Saída:

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)

Adicionar isso no meu arquivo bootstrap resolveu todos os meus problemas :D

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

aviso usando node-fetch em vez de fetch-headers resolveu o problema!

Esta página foi útil?
0 / 5 - 0 avaliações