Apollo-link-rest: Die Direktive `@rest` funktioniert nicht in einer SSR-Umgebung

Erstellt am 7. Jan. 2019  ·  9Kommentare  ·  Quelle: apollographql/apollo-link-rest

Hallo!

Ich habe eine @rest graphql-Abfrage, die perfekt in der Entwicklung/im Browser funktioniert, aber wenn ich denselben Code auf dem Server (SSR) ausführe, schlägt sie mit einer langen kryptischen Fehlermeldung fehl.

(Wenn ich die Direktive @rest deaktiviere, funktioniert alles andere einwandfrei, sowohl im Browser als auch in SSR).

Der Stacktrace:

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

Bitte um Rat :/

Hilfreichster Kommentar

Das Hinzufügen zu meiner Bootstrap-Datei löste alle meine Probleme: D

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

Beachten Sie, dass die Verwendung node-fetch anstelle von fetch-headers das Problem gelöst hat!

Alle 9 Kommentare

Ein bisschen mehr Graben, ich versuche nur, die Abfrage in NodeJS auszuführen, ohne mich um SSR usw.

und habe diesen 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)
                )

Mit diesem Ergebnis:

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 ist dieser Codeblock:

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

Auch wenn ich das fehlgeschlagene forEach damit ersetze:

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

Alles funktioniert wieder...

Die Herausforderung , ich verstehe den Zweck der obigen Zeilen nicht:

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

Da dies immer fehlschlagen und immer normalizeHeaders() ausführen würde?

Ich werde hier auf die Beine gehen und vermuten, dass Node sich über die Verwendung von new Headers() . Eine neue Headers-Instanz hat eine forEach -Methode für ihren Prototyp. Wenn dem Objekt, das zum Reduzieren bereitgestellt wird, eine forEach-Methode fehlt, führen wir normalizeHeaders aus. Dies bestimmt, ob die Variable eine Instanz von Headers ist. Wenn nicht, wird erneut ein neuer Satz von Headern mit new Headers() generiert, wodurch das Problem verursacht wird, das Sie hier sehen.

Ihr Object.keys(current).forEach() -Beispiel funktioniert, weil es ein einfaches Objekt erfolgreich in ein Array konvertiert, sodass Sie .forEach() darauf aufrufen können.

Nächster Schritt beim Debuggen ... was ist das Ergebnis von normalizeHeaders() in Ihrer SSR-Anwendung? Schauen Sie sich an, was es ausgibt, und ich vermute, Sie werden feststellen, dass es sich eher um ein einfaches Objekt als um eine Instanz von Headers handelt.

Soweit mir bekannt ist, glaube ich nicht, dass SSR vollständig getestet wurde (siehe Nr. 155), aber @fbartho kann wahrscheinlich mehr Licht ins Dunkel bringen.

SSR ist derzeit keine direkt getestete oder unterstützte Funktion. Begründung: Wenn Sie SSR haben, können Sie Ihren eigenen Apollo GraphQL-Server bereitstellen und benötigen keine Link-Rest

Einige Leute haben es jedoch herausgefunden. Und ich habe nichts gegen PRs einzuwenden, die den Zustand der Welt dort verbessern. Es scheint mir, dass wir vielleicht nur ein SSR-Tutorial brauchen.

Darüber hinaus ist es relativ üblich, Header abhängig von Ihrer Laufzeitumgebung mehrfach zu füllen. Hast du das schon probiert? @Richard87

Die Antwort von @ Tombarton ist korrekt: Der Zweck dieses Codes besteht darin, Header-Hashes in die entsprechende Headers-Klasse zu konvertieren.

Hallo zusammen!

Danke für die Aufmerksamkeit für mein Problem ;)

Der untere Teil meiner index.js ist dies:

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

Ich bin mir also ziemlich sicher, dass ich das Header-Polyfill habe (auch ohne die global.Headers habe ich ein paar andere Probleme bekommen) ...

Ich habe bereits einen GraphQL-Server, der fest in meine PHP-Anwendung integriert ist (API-Plattform + https://webonyx.github.io/graphql-php/). Daher möchte ich keinen zusätzlichen Apollo GraphQL-Server installieren, ich möchte nur um die React App schneller zu laden :D

Es fällt mir schwer, restLink.ts zu debuggen, aber ich habe ein paar strategische console.log $ eingefügt, aber es current _scheint_ ein Planobjekt zu sein, aber console.log könnten einige Informationen fehlen ...

Bearbeiten...

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

Druckt "Header" zweimal aus...

console.log(current.forEach) druckt Undefiniert aus, egal was, es scheint, dass meine Kopfzeilen-Polyfüllung keine forEach -Eigenschaft hat?

Mein PHPStorm sagt mir, dass headers eine forEach-Methode hat, aber wenn ich diesen Code im Knoten ausführe, bekomme ich ein 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))

Ausgabe:

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)

Das Hinzufügen zu meiner Bootstrap-Datei löste alle meine Probleme: D

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

Beachten Sie, dass die Verwendung node-fetch anstelle von fetch-headers das Problem gelöst hat!

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen