aws-mobile-appsync-sdk-js
aws-mobile-appsync-sdk-js copied to clipboard
Using HttpLink from apollo-angular-link-http
I am creating an Angular 6 app and I am trying to get server side rendering working. I am having a lot of trouble with getting the page to wait for the GraphQL calls to AppSync to finish before the page is rendered.
I suspected that the calls were being made outside the zone.js framework and so Angular could not detect when the calls were finished. And finally today I came across this which under the section titled "Server-side rendering" says that you must use HttpLink from apollo-angular-link-http for GraphQL to work correctly with zone.js.
So I looked at the source to aws-appsync and this issue to configure the AWSAppsyncClient to use the HttpLink from apollo-angular-link-http. See the code below.
However, this causes the GraphQL calls to fail. When I try in a web browser I simply get the following error and there is no call to app sync listed in the network tab of the Chrome browser:
ERROR Error: Uncaught (in promise): Error: Network error: undefined
When I run the server side code it give the same error but it shows a stack trace that gives a little more information. Down in the stack trace it has:
graphQLErrors: [], networkError: { body: [Object], url: 'https://XXXXXXXXXXXX.appsync-api.us-east-1.amazonaws.com/graphql', headers: [HttpHeaders], status: 404, statusText: 'Not Found' }, message: 'Network error: undefined', extraInfo: undefined }
How can I configure the AWSAppSyncClient to use the HttpLink so that zones works correctly?
Here is the code I use to create the AWSAppSync Client with the creation code that works (minus zones) commented out. Please excuse the messy code. I have been doing a lot of experimenting.
import AWSAppSyncClient, {createAppSyncLink} from 'aws-appsync';
import {environment} from "../../../environments/environment";
import {NormalizedCache} from "apollo-cache-inmemory";
import { ISignUpResult, CognitoUser, MFAOption, CognitoUserSession, CognitoUserAttribute } from 'amazon-cognito-identity-js';
import Auth from "@aws-amplify/auth";
import {Injectable} from '@angular/core';
import {HttpLink, HttpLinkModule} from 'apollo-angular-link-http';
import { onError } from 'apollo-link-error';
import {ApolloLink} from 'apollo-link';
export enum AppSyncServerType {
UnknownUser = "UnknownUser",
KnownUser = "KnownUser"
}
@Injectable({providedIn: 'root'})
export class AppSyncServerService {
// Static Private Variables
readonly sharedUnknown: AWSAppSyncClient<NormalizedCache>;
readonly sharedKnown: AWSAppSyncClient<NormalizedCache>;
// ========================================
// Private Methods
// ========================================
private async currentJwtToken(): Promise<string> {
const session: CognitoUserSession = await Auth.currentSession();
return session.getIdToken().getJwtToken();
}
private newClient(type: AppSyncServerType): AWSAppSyncClient<NormalizedCache> {
const env = environment.appsync[type];
// Create the auth config
const authConfig: any = {};
authConfig.type = env.authenticationType;
switch (env.authenticationType) {
case 'AWS_IAM':
authConfig.credentials = () => Auth.currentCredentials();
break;
case 'AMAZON_COGNITO_USER_POOLS':
authConfig.jwtToken = () => this.currentJwtToken();
break;
}
const apolloHttpLink = this.httpLink.create({uri: env.endpoint});
// return new AWSAppSyncClient({
// url: env.endpoint,
// region: env.region,
// auth: authConfig,
// disableOffline: true,
// });
const onErrorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({message, locations, path}) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) { console.log(`[Network error]: ${networkError}`); }
});
const appSyncLink = createAppSyncLink({
url: env.endpoint,
region: env.region,
auth: authConfig,
complexObjectsCredentials: () => Auth.currentCredentials(),
resultsFetcherLink: apolloHttpLink,
});
const link = ApolloLink.from([
appSyncLink,
// apolloHttpLink
]);
// @ts-ignore
return new AWSAppSyncClient({}, { link });
}
// ========================================
// Constructor
// ========================================
constructor(private httpLink: HttpLink) {
this.sharedUnknown = this.newClient(AppSyncServerType.UnknownUser);
this.sharedKnown = this.newClient(AppSyncServerType.KnownUser);
}
}
Hi @fcobia
And finally today I came across this which under the section titled "Server-side rendering" says that you must use HttpLink from apollo-angular-link-http for GraphQL to work correctly with zone.js.
Something like this might work:
const apolloHttpLink = httpLink.create({uri: env.endpoint}); // from apollo-angular-link-http
const appSyncLink = createAppSyncLink({
url: env.endpoint,
region: appSyncConfig.region,
auth: {
type: appSyncConfig.authenticationType,
apiKey: appSyncConfig.apiKey
},
resultsFetcherLink: apolloHttpLink
});
const client = new AWSAppSyncClient({
disableOffline: true
}, { link , ssrMode: true});
Can you try it and let us know how it goes?
@manueliglesias
Thank you. It seems to work. I had to modify the code you gave, because it doesn't compile, but I think I got it. The modified code is below. However, there appears to be a lot of duplicate config information, because both the createAppSyncLink and new AWSAppSyncClient calls seem to require all the same information. Is this correct? Is there a way to not duplicate all that information?
Also, and this may be completely unrelated to this code, but when I use this code I started getting 'Refused to set unsafe header "host"' errors in the console whenever a GraphQL call is made. Do you know why this is happening?
const apolloHttpLink = this.httpLink.create({uri: env.endpoint}); // from apollo-angular-link-http
const appSyncLink = createAppSyncLink({
url: env.endpoint,
region: env.region,
auth: authConfig,
complexObjectsCredentials: null,
resultsFetcherLink: apolloHttpLink
});
return new AWSAppSyncClient({
url: env.endpoint,
region: env.region,
auth: authConfig,
disableOffline: true,
offlineConfig: {
storage: localForage,
},
}, { link: appSyncLink , ssrMode: true});
@fcobia hey, did you come to a conclusion about why the error appears in the console? I have done the same stuff you did, and can not seem to be able to get rid of the error :(.
It has been a while and I don't remember. I am sure I must have because the project does work now. However, I don't remember what I did.
@fcobia no problem, thanks anyway. However, I am trying to get rid of aws-appsync-auth-link aws-appsync-subscription-link By copy pasting the whole code without the place where they are setting the host, path, ... pretty much the unsafe headers.