Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

aws-appsync-auth-link + Next.js produces TypeError: forward is not a function error #473

Closed
sakhmedbayev opened this issue Oct 17, 2019 · 6 comments

Comments

@sakhmedbayev
Copy link

Do you want to request a feature or report a bug?
bug

What is the current behavior?
Following this example, I am trying to initialize apollo client using Next.js. Here is an example I follow to make it work except my lib/initApolloClient.ts looks like the following:

import { awsmobile } from "@teloscom/ecomapiawsamp";
import { NormalizedCacheObject } from "apollo-boost";
import { InMemoryCache } from "apollo-cache-inmemory";
import ApolloClient from "apollo-client";
import { ApolloLink } from "apollo-link";
import Amplify, { Auth } from "aws-amplify";
import { AUTH_TYPE } from "aws-appsync";
import { createAuthLink } from "aws-appsync-auth-link";
import fetch from "isomorphic-unfetch";


Amplify.configure({
  ...awsmobile
});

let apolloClient: ApolloClient<any> | null = null;

// Polyfill fetch() on the server (used by apollo-client)
if (!process.browser) {
  // @ts-ignore
  global.fetch = fetch;
}

function createApolloClient(initialState: NormalizedCacheObject) {
  const ssrMode = !process.browser

  const url = awsmobile.aws_appsync_graphqlEndpoint;
  const region = awsmobile.aws_appsync_region;
  const auth: any = {
    type: AUTH_TYPE.AWS_IAM,
    credentials: () => Auth.currentCredentials()
  }

  const link = ApolloLink.from([
    createAuthLink({ url, region, auth }),
  ]);

  const client = new ApolloClient({
    link,
    cache: new InMemoryCache(),
    ssrMode
  });

  if (initialState) {
    client.cache = new InMemoryCache().restore(initialState);
  }

  return client;
}

export default function initApollo(initialState: any = {}) {
  // Make sure to create a new client for every server-side request so that data
  // isn't shared between connections (which would be bad)
  if (!process.browser) {
    return createApolloClient(initialState);
  }

  // Reuse client on the client-side
  if (!apolloClient) {
    apolloClient = createApolloClient(initialState);
  }

  return apolloClient;
}

All the above produces the following error:

(node:19313) UnhandledPromiseRejectionWarning: TypeError: forward is not a function
    at Object.<anonymous> (/Users/np/mine/telos/ecomcntrls/node_modules/aws-appsync-auth-link/lib/auth-link.js:154:43)
    at step (/Users/np/mine/telos/ecomcntrls/node_modules/aws-appsync-auth-link/lib/auth-link.js:56:23)
    at Object.next (/Users/np/mine/telos/ecomcntrls/node_modules/aws-appsync-auth-link/lib/auth-link.js:37:53)
    at fulfilled (/Users/np/mine/telos/ecomcntrls/node_modules/aws-appsync-auth-link/lib/auth-link.js:28:58)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:19313) 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: 4)

Which versions and which environment (browser, react-native, nodejs) / OS are affected by this issue? Did this work in previous versions?

here is my complete package.json file:

{
  "name": "with-typescript",
  "version": "1.0.0",
  "scripts": {
    "dev": "next",
    "build": "next build",
    "start": "next start",
    "type-check": "tsc"
  },
  "dependencies": {
    "@apollo/react-ssr": "^3.1.3",
    "@teloscom/ecomapiawsamp": "^1.0.12", // this is private, you cannot install it
    "apollo-boost": "^0.4.4",
    "apollo-cache-inmemory": "^1.6.3",
    "apollo-client": "^2.6.4",
    "apollo-link": "^1.2.13",
    "aws-amplify": "^1.2.2",
    "aws-appsync": "^2.0.1",
    "aws-appsync-auth-link": "^1.0.1",
    "isomorphic-unfetch": "3.0.0",
    "next": "latest",
    "react": "^16.10.1",
    "react-apollo-hooks": "^0.5.0",
    "react-dom": "^16.10.1"
  },
  "devDependencies": {
    "@types/next": "^8.0.6",
    "@types/node": "^12.7.8",
    "@types/react": "^16.9.3",
    "@types/react-dom": "^16.9.1",
    "typescript": "3.6.3"
  },
  "license": "ISC"
}
@sakhmedbayev
Copy link
Author

This issue can be resolved if you add createHttpLink({ uri: url }), to ApolloLink config. The doc here is incorrect.

@dai-shi
Copy link

dai-shi commented Oct 17, 2019

As I understand:

  1. this works
const link = ApolloLink.from([
  createAuthLink({ url, region, auth }),
  createSubscriptionHandshakeLink(url, httpLink)
]);
  1. this doesn't
const link = ApolloLink.from([
  createAuthLink({ url, region, auth }),
]);
  1. but this works
const link = ApolloLink.from([
  createAuthLink({ url, region, auth }),
  httpLink,
]);

This is reasonable if you know the link is split in createSubscriptionHandshakeLink.

@sakhmedbayev
Copy link
Author

sakhmedbayev commented Oct 17, 2019

Yep, you are right. My fault. Thanks, @dai-shi!

@sakhmedbayev
Copy link
Author

sakhmedbayev commented Oct 17, 2019

@dai-shi, perhaps you can help me here. I am trying to initialize apollo-client as I described above. Everything works client-side but not on the server. I get this error:

Error while running `getMarkupFromTree` { Error: Network error: Response not successful: Received status code 401
    at new ApolloError (/Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:92:26)
    at /Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:1587:34
    at /Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:2007:15
    at Set.forEach (<anonymous>)
    at /Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:2005:26
    at Map.forEach (<anonymous>)
    at QueryManager.broadcastQueries (/Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:2003:20)
    at /Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-client/bundle.umd.js:1482:29
    at process._tickCallback (internal/process/next_tick.js:68:7)
  graphQLErrors: [],
  networkError:
   { ServerError: Response not successful: Received status code 401
       at Object.exports.throwServerError (/Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-link-http-common/lib/index.js:23:17)
       at /Users/saidakhmedbayev/np/mine/telos/ecomcntrls/node_modules/apollo-link-http-common/lib/index.js:48:21
       at process._tickCallback (internal/process/next_tick.js:68:7)
     name: 'ServerError',
     response:
      Response {
        size: 0,
        timeout: 0,
        [Symbol(Body internals)]: [Object],
        [Symbol(Response internals)]: [Object] },
     statusCode: 401,
     result: { errors: [Array] } },
  message:
   'Network error: Response not successful: Received status code 401',
  extraInfo: undefined }
[ event ] disposing inactive page(s): /next/dist/pages/_error
[ info ]  bundled successfully, waiting for typecheck results ...
[ ready ] compiled successfully - ready on http://localhost:3000

and here is how my withApollo.tsx looks like:

import React from "react";
import initApollo from "./initApollo";
import Head from "next/head";
import { renderToString } from "react-dom/server";
// import { NextAppContext } from "next";
import { ApolloClient } from "apollo-boost";
import { getMarkupFromTree } from "react-apollo-hooks";

// import { getDataFromTree } from "@apollo/react-ssr";


export default (
  App: React.ComponentType<any> & { getInitialProps?: Function }
) => {
  return class Apollo extends React.Component {
    static displayName = "withApollo(App)";
    apolloClient: ApolloClient<any>;
    static async getInitialProps(ctx: any) {
      const { Component, router } = ctx;

      let appProps = {};
      if (App.getInitialProps) {
        appProps = await App.getInitialProps(ctx);
      }

      // Run all GraphQL queries in the component tree
      // and extract the resulting data
      const apollo = initApollo();
      if (!process.browser) {
        try {
          // Run all GraphQL queries
          await getMarkupFromTree({
            renderFunction: renderToString,
            tree: (
              <App
                {...appProps}
                Component={Component}
                router={router}
                apolloClient={apollo}
              />
            )
          });

          // await getDataFromTree(
          //   <App
          //     {...appProps}
          //     Component={Component}
          //     router={router}
          //     apolloClient={apollo}
          //   />
          // );
        } catch (error) {
          // Prevent Apollo Client GraphQL errors from crashing SSR.
          // Handle them in components via the data.error prop:
          // https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-query-data-error
          console.error("Error while running `getMarkupFromTree`", error);
        }

        // getDataFromTree does not call componentWillUnmount
        // head side effect therefore need to be cleared manually
        Head.rewind();
      }

      // Extract query data from the Apollo store
      const apolloState = apollo.cache.extract();

      return {
        ...appProps,
        apolloState
      };
    }

    constructor(props: any) {
      super(props);
      this.apolloClient = initApollo(props.apolloState);
    }

    render() {
      return <App {...this.props} apolloClient={this.apolloClient} />;
    }
  };
};

so this part of withApollo.tsx does not work:

 await getMarkupFromTree({
            renderFunction: renderToString,
            tree: (
              <App
                {...appProps}
                Component={Component}
                router={router}
                apolloClient={apollo}
              />
            )
          });

@dai-shi
Copy link

dai-shi commented Oct 17, 2019

Unfortunately, I don't have any clue at first glance. So, it works without appsync auth link, does it?

@romainquellec
Copy link

When it's working, can you make a PR on next repo @sakhmedbayev ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants