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"
}
This issue can be resolved if you add createHttpLink({ uri: url }), to ApolloLink config. The doc here is incorrect.
As I understand:
1) this works
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
createSubscriptionHandshakeLink(url, httpLink)
]);
2) this doesn't
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
]);
3) but this works
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
httpLink,
]);
This is reasonable if you know the link is split in createSubscriptionHandshakeLink.
Yep, you are right. My fault. Thanks, @dai-shi!
@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}
/>
)
});
Unfortunately, I don't have any clue at first glance. So, it works without appsync auth link, does it?
When it's working, can you make a PR on next repo @sakhmedbayev ?
Most helpful comment
As I understand:
1) this works
2) this doesn't
3) but this works
This is reasonable if you know the link is split in createSubscriptionHandshakeLink.