¡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 :/
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 ...
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!
Esto debe agregarse a los documentos de apollo-link-rest . ¿A qué archivo debo subir PR? https://github.com/apollographql/apollo-link-rest/blob/master/README.md , https://github.com/apollographql/apollo-link-rest/blob/master/docs/rest.md o https://github.com/apollographql/apollo-link/blob/master/docs/source/links/rest.md ?
Comentario más útil
Agregar esto en mi archivo de arranque resolvió todos mis problemas: D
¡Observe que usar
node-fetch
en lugar defetch-headers
resolvió el problema!