AWS Appsync SDK Bindings


#1

Hello everyone.

I’m trying to use https://github.com/awslabs/aws-mobile-appsync-sdk-js.

I used the bindings from idkjs (https://reasonml.chat/t/writing-bindings/598/8) for the client and it works.

But I need to add a auth header to all the requests to add a JWT. This token needs to be passed through Appsync to the REST API used in some of the resolvers.

I’m trying to use a customLink instead of the normal client : https://github.com/awslabs/aws-mobile-appsync-sdk-js/blob/master/packages/aws-appsync/src/client.ts#L225

This is what I have so far.

Client.re

[@bs.module]
external introspectionQueryResultData: Js.t({…}) =
"…/…/fragmentTypes.json";

let fragmentMatcher =
ApolloInMemoryCache.createIntrospectionFragmentMatcher(
~data=introspectionQueryResultData,
);

let cache = ApolloInMemoryCache.createInMemoryCache(~fragmentMatcher);

let httpLink =
ApolloLinks.createHttpLink(
~uri=
https://apiurl”,
(),
);

let authLink = {
let authHandler = () => {
let token = “jwt”;
{
“headers”: {
“Authorization”: {j|Bearer $token|j},
},
};
};

ApolloLinks.createContextLink(authHandler);
};

let apolloLinks = ApolloLinks.from([|authLink, httpLink|]);

AppSyncClient.re

module AppSyncConfig = {
type config = {
.
“graphqlEndpoint”: string,
“region”: string,
“authenticationType”: string,
“apiKey”: string,
};
let config = {
“graphqlEndpoint”: “url”,
“region”: “eu-west-1”,
“authenticationType”: “API_KEY”,
“apiKey”: “key”,
};
};

module AppSyncClient = {
[@bs.module “aws-appsync”] [@bs.new]
external makeAWSAppSyncClient:
(
{
.
“url”: Js.Nullable.t(string),
“region”: Js.Nullable.t(string),
“auth”:
Js.Nullable.t({
.
“type”: string,
“apiKey”: string,
}),
},
{. “link”: Js.Nullable.t(unit => 'a)}
) =>
'a =
“AWSAppSyncClient”;

let createAWSAppSyncClient = (~url=?, ~region=?, ~auth=?, ~link=?, ()) => {
let appSyncClientOptions = {
“url”: Js.Nullable.fromOption(url),
“region”: Js.Nullable.fromOption(region),
“auth”: Js.Nullable.fromOption(auth),
};

makeAWSAppSyncClient(
  appSyncClientOptions,
  {"link": Js.Nullable.fromOption(link)},
);

};
};

module AppSyncLink = {
[@bs.module “aws-appsync”] [@bs.new]
external makeAWSAppSyncLink:
{
.
“url”: Js.Nullable.t(string),
“region”: Js.Nullable.t(string),
“auth”:
Js.Nullable.t({
.
“type”: string,
“apiKey”: string,
}),
“resultsFetcherLink”: Js.Nullable.t(ReasonApolloTypes.apolloLink),
} =>
'b =
“createAppSyncLink”;

let createAWSAppSyncLink =
(~url=?, ~region=?, ~auth=?, ~resultsFetcherLink=?, ()) => {
let appSyncLinkOptions = {
“url”: Js.Nullable.fromOption(url),
“region”: Js.Nullable.fromOption(region),
“auth”: Js.Nullable.fromOption(auth),
“resultsFetcherLink”: Js.Nullable.fromOption(resultsFetcherLink),
};
makeAWSAppSyncLink(appSyncLinkOptions);
};
};

let awsLink =
AppSyncLink.createAWSAppSyncLink(
~url=AppSyncConfig.config##graphqlEndpoint,
~region=AppSyncConfig.config##region,
~auth={
“type”: AppSyncConfig.config##authenticationType,
“apiKey”: AppSyncConfig.config##apiKey,
},
~resultsFetcherLink=Client.apolloLinks,
);

let client =
AppSyncClient.createAWSAppSyncClient(
~url=AppSyncConfig.config##graphqlEndpoint,
~region=AppSyncConfig.config##region,
~auth={
“type”: AppSyncConfig.config##authenticationType,
“apiKey”: AppSyncConfig.config##apiKey,
},
// ~link=awsLink,
(),
);

And :

ReactDOMRe.renderToElementWithId(
<ReasonApollo.Provider client=AppSyncClient.client>

</ReasonApollo.Provider>,
“app”,
);

In these bindings I’m troubled with :
{. “link”: Js.Nullable.t(unit => 'a)}

Is this type normal? I it the result of calling createAppsyncLink so Im not sure the return type should be this?

If I comment out the ~link in the “let client” it works.
I get a response from the API saying 401 unauthaurized.

If I uncomment ~link, I dont get a response anymore and get an error in the js console:

Uncaught (in promise) TypeError: Cannot read property ‘_subscription’ of undefined
at error (Observable.js:224)

Im just tryin a simple query (no subscriptions) in the app.

Does anyone use Appsync with Reason and use customLink to create a client?

Maybe the error is from the API but I would like to be sure the client is good.

Thanks for your help.


Writing Bindings
#2

Custom Links arent very well supported by AppSync - there is a lot of weirdness around configuration getting overwritten in unexpected ways. It’s really just a (bad) wrapper around an old version of Apollo which is a bit maddening. See my personal hell from this past winter: https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/359

So it’s really hard to help folks with problems. For example, because you are using a custom link, you don’t have to actually pass in auth information to CreatClient!

This is a totally valid to do (js):

const client = new AWSAppSyncClient(
  { disableOffline: true },
  { link, cache }
);

The good news (as you’ve seen) is that the AppSync client is written in typescript so I’d suggest digging into the aws code to see how any typing problems might be resolved on the reason side.

I also suggest looking at the generated js! Bucklescripts biggest strength is that it generates readable js, and sometimes it gives you hints in interop situations like this that you mistyped or miscast a type to JS. For example, you might see an array (a compiled record) where JS would expect an object with keys.

I’m sorry I can’t be more helpful, but it really looks like your problems are with AppSync not the reason compiler. M There may also be some bad bindings here for this use case, but its hard to tell without seeing the generated js.