Apollo-link-rest: Директива `@rest` не работает в среде SSR

Созданный на 7 янв. 2019  ·  9Комментарии  ·  Источник: apollographql/apollo-link-rest

Привет!

У меня есть запрос graphql @rest , который отлично работает в разработке/браузере, но когда я запускаю тот же код на сервере (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.

Пожалуйста посоветуй :/

Самый полезный комментарий

Добавление этого в мой загрузочный файл решило все мои проблемы: D

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

обратите внимание, что использование node-fetch вместо fetch-headers решило проблему!

Все 9 Комментарий

Еще немного копания, я просто пытаюсь выполнить запрос в NodeJS, не беспокоясь о SSR и т. д.,

и есть этот код:

            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 в своем прототипе. Если в объекте, предоставленном для сокращения, отсутствует метод forEach, мы выполняем normalizeHeaders . Это определяет, является ли переменная экземпляром заголовков. Если нет, он снова сгенерирует новый набор заголовков, используя new Headers() , что вызовет проблему, которую вы видите здесь.

Ваш пример Object.keys(current).forEach() работает, потому что он успешно преобразует простой объект в массив, что позволяет вам вызывать на нем .forEach() .

Следующий шаг отладки... каков результат normalizeHeaders() в вашем приложении SSR? Взгляните на то, что он выводит, и я предполагаю, что вы обнаружите, что это обычный объект, а не экземпляр заголовков.

Насколько я знаю, я не верю, что SSR был полностью протестирован (см. № 155), но @fbartho , вероятно, может пролить больше света на это.

В настоящее время функция SSR не тестируется и не поддерживается напрямую. Обоснование: если у вас есть SSR, вы можете развернуть свой собственный сервер Apollo GraphQL, и вам не нужен link-rest

Однако кое-кто догадался. И я не возражаю против пиара, улучшающего там состояние мира. Мне кажется, что нам, возможно, просто нужен учебник по SSR.

Кроме того, относительно часто требуется полифиллировать заголовки в зависимости от вашей среды выполнения. Вы уже пробовали это? @ Ричард87

Ответ @tombarton правильный: цель этого кода - преобразовать хэши заголовков в соответствующий класс заголовков.

Всем привет!

Спасибо за внимание к моей проблеме ;)

Нижняя часть моего 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 у меня есть куча других проблем)...

У меня уже есть сервер GraphQL, тесно интегрированный в мое приложение PHP (API-платформа + https://webonyx.github.io/graphql-php/). Поэтому я не хочу устанавливать дополнительный сервер Apollo GraphQL, я просто хочу чтобы быстрее загрузить приложение React :D

Мне трудно отлаживать restLink.ts, но я добавил несколько стратегических console.log , но это current _кажется_ объектом плана, но 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());
};

Выводит "Заголовок" дважды...

console.log(current.forEach) выводит Undefined несмотря ни на что, кажется, что мой полифилл заголовков не имеет свойства 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;
}

обратите внимание, что использование node-fetch вместо fetch-headers решило проблему!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги