React 中使用 ApolloClient 的 AppSync 订阅
AppSync subscriptions with ApolloClient in React
我目前正在使用 ApolloClient 连接到 AppSync GraphQL API。这一切都非常适合查询和突变,但我在订阅工作时遇到了一些问题。我遵循了 Apollo 文档,我的 App.js 看起来像这样:
import React from 'react';
import './App.css';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from '@apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink, split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { createAuthLink } from 'aws-appsync-auth-link';
import { createHttpLink } from 'apollo-link-http';
import AWSAppSyncClient, { AUTH_TYPE } from "aws-appsync";
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const url = "https://xxx.appsync-api.eu-west-2.amazonaws.com/graphql"
const realtime_url = "wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql"
const region = "eu-west-2";
const auth = {
type: AUTH_TYPE.API_KEY,
apiKey: process.env.REACT_APP_API_KEY
};
const wsLink = new WebSocketLink({
uri: realtime_url,
options: {
reconnect: true
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
ApolloLink.from([
createAuthLink({ realtime_url, region, auth }),
wsLink
]),
ApolloLink.from([
createAuthLink({ url, region, auth }),
createHttpLink({ uri: url })
])
);
const client = new ApolloClient({
link: link,
cache: new InMemoryCache({
dataIdFromObject: object => object.id,
}),
});
function Page() {
const { loading, error, data } = useSubscription(
gql`
subscription questionReleased {
questionReleased {
id
released_date
}
}
`
)
if (loading) return <span>Loading...</span>
if (error) return <span>Error!</span>
if (data) console.log(data)
return (
<div>{data}</div>
);
}
function App() {
return (
<ApolloProvider client={client}>
<div className="App">
<Page />
</div>
</ApolloProvider>
);
}
export default App;
如果我转到网络检查器中的网络选项卡,我可以看到请求:
wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql
以及消息:
{"type":"connection_init","payload":{}}
{"id":"1","type":"start","payload":{"variables":{},"extensions":{},"operationName":"questionReleased","query":"subscription questionReleased {\n questionReleased {\n id\n released_date\n __typename\n }\n}\n"}}
{"id":"2","type":"start","payload":{"variables":{},"extensions":{},"operationName":"questionReleased","query":"subscription questionReleased {\n questionReleased {\n id\n released_date\n __typename\n }\n}\n"}}
{"payload":{"errors":[{"message":"Both, the \"header\", and the \"payload\" query string parameters are missing","errorCode":400}]},"type":"connection_error"}
我搜索了很多,似乎 ApolloClient 可能与 AppSync 订阅不兼容 - 有人能证实这一点吗?
因此,作为替代方案,我尝试使用 AWSAppSyncClient
进行订阅:
function Page() {
const aws_client = new AWSAppSyncClient({
region: "eu-west-2",
url: realtime_url,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey: process.env.REACT_APP_API_KEY
},
disableOffline: true
});
const { loading, error, data } = useSubscription(
gql`
subscription questionReleased {
questionReleased {
id
released_date
}
}
`,
{client: aws_client}
)
if (loading) return <span>Loading...</span>
if (error) return <span>Error!</span>
if (data) console.log(data)
return (
<div>{data}</div>
);
}
它现在发送带有请求的查询字符串:
wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql?header=eyJob3N0I...&payload=e30=
现在我得到了一个不同的错误:
{"type":"connection_init"}
{"payload":{"errors":[{"errorType":"HttpNotFoundException"}]},"type":"connection_error"}
我已经仔细检查了 url,没问题(如果不是,你会得到 ERR_NAME_NOT_RESOLVED
)。当我通过 AppSync 控制台手动 运行 时,订阅有效,所以这也应该没问题。
我也在 aws_client
上尝试了 .hydrated()
,但出现另一个错误 (TypeError: this.refreshClient(...).client.subscribe is not a function
)
我做错了什么?这几天让我抓狂!
发帖没多久我终于想通了。我会把解决方案放在这里,以防其他人遇到同样的问题。首先 AWSAppSyncClient
应该采用主 graphql url 而不是实时 url - 它可以计算出实时 url 本身。但是我仍然无法使用 useSubscription
挂钩,只能通过调用 aws_client.subscribe()
.
为了让它与 useSubscription
钩子一起工作,我找到了这个讨论中提到的解决方案:
https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/450
相关代码为:
import { createSubscriptionHandshakeLink } from 'aws-appsync-subscription-link';
const httpLink = createHttpLink({ uri: url })
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
createSubscriptionHandshakeLink(url, httpLink)
]);
使用之后,一切对我来说都很完美。
我目前正在使用 ApolloClient 连接到 AppSync GraphQL API。这一切都非常适合查询和突变,但我在订阅工作时遇到了一些问题。我遵循了 Apollo 文档,我的 App.js 看起来像这样:
import React from 'react';
import './App.css';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from '@apollo/react-hooks';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink, split } from 'apollo-link';
import { WebSocketLink } from 'apollo-link-ws';
import { getMainDefinition } from 'apollo-utilities';
import { createAuthLink } from 'aws-appsync-auth-link';
import { createHttpLink } from 'apollo-link-http';
import AWSAppSyncClient, { AUTH_TYPE } from "aws-appsync";
import { useSubscription } from '@apollo/react-hooks';
import { gql } from 'apollo-boost';
const url = "https://xxx.appsync-api.eu-west-2.amazonaws.com/graphql"
const realtime_url = "wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql"
const region = "eu-west-2";
const auth = {
type: AUTH_TYPE.API_KEY,
apiKey: process.env.REACT_APP_API_KEY
};
const wsLink = new WebSocketLink({
uri: realtime_url,
options: {
reconnect: true
},
});
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
ApolloLink.from([
createAuthLink({ realtime_url, region, auth }),
wsLink
]),
ApolloLink.from([
createAuthLink({ url, region, auth }),
createHttpLink({ uri: url })
])
);
const client = new ApolloClient({
link: link,
cache: new InMemoryCache({
dataIdFromObject: object => object.id,
}),
});
function Page() {
const { loading, error, data } = useSubscription(
gql`
subscription questionReleased {
questionReleased {
id
released_date
}
}
`
)
if (loading) return <span>Loading...</span>
if (error) return <span>Error!</span>
if (data) console.log(data)
return (
<div>{data}</div>
);
}
function App() {
return (
<ApolloProvider client={client}>
<div className="App">
<Page />
</div>
</ApolloProvider>
);
}
export default App;
如果我转到网络检查器中的网络选项卡,我可以看到请求:
wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql
以及消息:
{"type":"connection_init","payload":{}}
{"id":"1","type":"start","payload":{"variables":{},"extensions":{},"operationName":"questionReleased","query":"subscription questionReleased {\n questionReleased {\n id\n released_date\n __typename\n }\n}\n"}}
{"id":"2","type":"start","payload":{"variables":{},"extensions":{},"operationName":"questionReleased","query":"subscription questionReleased {\n questionReleased {\n id\n released_date\n __typename\n }\n}\n"}}
{"payload":{"errors":[{"message":"Both, the \"header\", and the \"payload\" query string parameters are missing","errorCode":400}]},"type":"connection_error"}
我搜索了很多,似乎 ApolloClient 可能与 AppSync 订阅不兼容 - 有人能证实这一点吗?
因此,作为替代方案,我尝试使用 AWSAppSyncClient
进行订阅:
function Page() {
const aws_client = new AWSAppSyncClient({
region: "eu-west-2",
url: realtime_url,
auth: {
type: AUTH_TYPE.API_KEY,
apiKey: process.env.REACT_APP_API_KEY
},
disableOffline: true
});
const { loading, error, data } = useSubscription(
gql`
subscription questionReleased {
questionReleased {
id
released_date
}
}
`,
{client: aws_client}
)
if (loading) return <span>Loading...</span>
if (error) return <span>Error!</span>
if (data) console.log(data)
return (
<div>{data}</div>
);
}
它现在发送带有请求的查询字符串:
wss://xxx.appsync-realtime-api.eu-west-2.amazonaws.com/graphql?header=eyJob3N0I...&payload=e30=
现在我得到了一个不同的错误:
{"type":"connection_init"}
{"payload":{"errors":[{"errorType":"HttpNotFoundException"}]},"type":"connection_error"}
我已经仔细检查了 url,没问题(如果不是,你会得到 ERR_NAME_NOT_RESOLVED
)。当我通过 AppSync 控制台手动 运行 时,订阅有效,所以这也应该没问题。
我也在 aws_client
上尝试了 .hydrated()
,但出现另一个错误 (TypeError: this.refreshClient(...).client.subscribe is not a function
)
我做错了什么?这几天让我抓狂!
发帖没多久我终于想通了。我会把解决方案放在这里,以防其他人遇到同样的问题。首先 AWSAppSyncClient
应该采用主 graphql url 而不是实时 url - 它可以计算出实时 url 本身。但是我仍然无法使用 useSubscription
挂钩,只能通过调用 aws_client.subscribe()
.
为了让它与 useSubscription
钩子一起工作,我找到了这个讨论中提到的解决方案:
https://github.com/awslabs/aws-mobile-appsync-sdk-js/issues/450
相关代码为:
import { createSubscriptionHandshakeLink } from 'aws-appsync-subscription-link';
const httpLink = createHttpLink({ uri: url })
const link = ApolloLink.from([
createAuthLink({ url, region, auth }),
createSubscriptionHandshakeLink(url, httpLink)
]);
使用之后,一切对我来说都很完美。