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

Using HttpLink from apollo-angular-link-http #263

Open
fcobia opened this issue Oct 3, 2018 · 5 comments
Open

Using HttpLink from apollo-angular-link-http #263

fcobia opened this issue Oct 3, 2018 · 5 comments
Labels
investigating Amplify is looking into the issue

Comments

@fcobia
Copy link

fcobia commented Oct 3, 2018

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);
	}
}
@elorzafe elorzafe added the investigating Amplify is looking into the issue label Oct 4, 2018
@manueliglesias
Copy link
Contributor

manueliglesias commented Feb 4, 2019

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?

@fcobia
Copy link
Author

fcobia commented Feb 7, 2019

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

@papagei-ma
Copy link

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

@fcobia
Copy link
Author

fcobia commented Jan 24, 2022

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.

@papagei-ma
Copy link

papagei-ma commented Jan 24, 2022

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
investigating Amplify is looking into the issue
Projects
None yet
Development

No branches or pull requests

4 participants