Apollo-link-rest: La directiva `@rest` no funciona en el entorno SSR

Creado en 7 ene. 2019  ·  9Comentarios  ·  Fuente: apollographql/apollo-link-rest

¡Hola!

Tengo una consulta de graphql @rest que funciona perfectamente en desarrollo/navegador, pero cuando ejecuto el mismo código en el servidor (SSR), falla con un largo mensaje de error críptico.

(si desactivo la directiva @rest , todo lo demás funciona perfectamente, tanto en el navegador como en SSR).

El seguimiento de la pila:

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

Por favor aconséjame :/

Comentario más útil

Agregar esto en mi archivo de arranque resolvió todos mis problemas: D

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

¡Observe que usar node-fetch en lugar de fetch-headers resolvió el problema!

Todos 9 comentarios

Un poco más de excavación, solo estoy tratando de ejecutar la consulta en NodeJS, sin preocuparme por SSR, etc.

y tener este codigo:

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

Con 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 es este bloque 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

Además, si reemplazo el forEach que falla con esto:

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

Todo vuelve a funcionar...

El desafío , no entiendo el propósito de las líneas anteriores:

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

Dado que esto siempre fallaría y siempre ejecutaría normalizeHeaders() ?

Voy a arriesgarme aquí y supongo que Node se está molestando por el uso de new Headers() . Una nueva instancia de Headers tiene un método forEach en su prototipo. Si al objeto proporcionado para reducir le falta un método forEach, ejecutamos normalizeHeaders . Esto determina si la variable es una instancia de encabezados. De lo contrario, generará un nuevo conjunto de encabezados, nuevamente, usando new Headers() , lo que causará el problema que está viendo aquí.

Su ejemplo Object.keys(current).forEach() está funcionando porque está convirtiendo con éxito un objeto simple en una matriz, lo que le permite llamar a .forEach() en él.

Siguiente paso: depuración inteligente... ¿cuál es el resultado de normalizeHeaders() en su aplicación SSR? Eche un vistazo a lo que genera y supongo que encontrará que es un objeto simple, en lugar de una instancia de encabezados.

Hasta donde yo sé, no creo que SSR se haya probado por completo (ver #155), pero @fbartho probablemente pueda arrojar más luz sobre esto.

SSR no es una función directamente probada o admitida en este momento. Justificación: si tiene SSR, puede implementar su propio servidor Apollo GraphQL y no necesita el descanso de enlaces

Sin embargo, algunas personas lo han descubierto. Y no me opongo a las relaciones públicas que mejoran el estado del mundo allí. Me parece que tal vez solo necesitamos un tutorial de SSR.

Además, es relativamente común necesitar Polyfill Headers dependiendo de su entorno de tiempo de ejecución. ¿Ya lo has probado? @Richard87

La respuesta de @tombarton es correcta: el propósito de ese código es convertir hashes de encabezado en la clase de encabezados adecuada.

¡Hola a todos!

Gracias por la atención a mi problema ;)

La parte inferior de mi index.js es 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');

Así que estoy bastante seguro de que tengo el polyfill de encabezado (también sin el global.Headers tengo un montón de otros problemas)...

Ya tengo un servidor GraphQL funcionando perfectamente integrado en mi aplicación PHP (API-Platform + https://webonyx.github.io/graphql-php/) Así que no quiero instalar un servidor Apollo GraphQL adicional, solo quiero para cargar la aplicación React más rápido :D

Tengo dificultades para depurar restLink.ts, pero puse algunos console.log estratégicos pero current _parece_ ser un objeto del plan, pero console.log podría perder alguna información ...

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 "Encabezado" dos veces...

console.log(current.forEach) se imprime Indefinido sin importar qué, ¿parece que mi polyfill de encabezados no tiene una propiedad forEach ?

Mi PHPStorm me dice que headers tiene un método forEach, pero cuando ejecuto este código en el nodo obtengo 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))

Producción:

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)

Agregar esto en mi archivo de arranque resolvió todos mis problemas: D

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

¡Observe que usar node-fetch en lugar de fetch-headers resolvió el problema!

¿Fue útil esta página
0 / 5 - 0 calificaciones