Websocket reconnection loop, graphql_flutter - flutter

I am using graphql_flutter package in my app. This is the code for my client:
HttpLink httpLink = HttpLink(
uri: 'https://*******/graphql',
);
WebSocketLink webSocketLink = WebSocketLink(
url: "wss://*******/graphql/websocket/",
config: SocketClientConfig(
autoReconnect: true,
inactivityTimeout: Duration(seconds: 30),
),
);
AuthLink authLink = AuthLink(
getToken: ()async{
print(await SharedPreferencesHelper.getAuthenticationToken());
return "Bearer ${await SharedPreferencesHelper.getAuthenticationToken()}";
}
);
Link link = authLink.concat(httpLink);
link = link.concat(webSocketLink);
client = ValueNotifier(
GraphQLClient(
cache: InMemoryCache(),
link: link,
),
);
However whenever I create a subscription like this:
client.value.subscribe(Operation(
document: Subscriptions.chatMessageReceived,
variables: {
"receiverId": *******
}
)).listen((fetchResult){
print(fetchResult.data);
});
I get this log in repetition:
Connecting to websocket: wss://******/graphql/websocket/... flutter:
Connected to websocket. flutter: Disconnected from websocket. flutter:
Scheduling to connect in 5 seconds... flutter: Connecting to
websocket: wss://******/graphql/websocket/...
Even though everything works fine in graphql playground. What can it be?

this is because of inactivityTimeout: Duration(seconds: 30), in WebSocketLink config
you can increase the Duration to prevent it from auto disconnecting.

Related

graphql-flutter subscriptions in flutter connectivity issue

I am new to flutter development but I have good experience in nodejs and graphql. I am trying to consume the subscription widget of graphql-flutter and update the changes. but the connection is not being established. But I could use the query and Mutation widget and get the results. The examples provided by the graphql-flutter team is 2 years old and same with https://hasura.io/ documents. Can someone help me providing the latest examples or samples.
graphql-flutter:^5.0.0
If additional infos needed please comment below.
Thanks in advance
I made a class that I use with graphql but it'll be able to work with graphql-flutter but passing the client to
return GraphQLProvider(
client: Services().graphQL.client, // just how I have my services set up
child: MaterialApp(
title: 'Flutter Demo',
...
),
);
class:
class GraphQL {
static final HttpLink _httpLink = HttpLink(environment[envMode]!['server']);
static WebSocketLink _wsLink(String token) => WebSocketLink(
environment[envMode]!['webSocket'],
config: SocketClientConfig(
inactivityTimeout: const Duration(hours: 1),
initialPayload: {
'Authorization': 'Bearer $token',
},
),
);
Link _splitLink(String token) => Link.split(
(request) => request.isSubscription,
_wsLink(token),
_httpLink,
);
GraphQLClient client(String token) {
return GraphQLClient(
link: AuthLink(getToken: () async => 'Bearer $token')
.concat(_splitLink(token)),
cache: GraphQLCache(
store: HiveStore(),
),
);
}
Future<void> initHive() async {
return await initHiveForFlutter();
}
}
The environment and envMode come from a config file that has gets its data from an env file to keep manage env and secrets.

WebSocketException: Connection to url.. was not upgraded to websocket. It is working fine in angular but not in flutter why?

I have tried to do in 2 ways it's not working while it is working in angular
way
var channel = IOWebSocketChannel.connect(
Uri.parse('wss://......./...../mywebsockets'),
headers: {
'Connection': 'Upgrade',
'Upgrade': 'websocket'
}
);
channel.stream.listen((event) {
print(event);
});
2nd way
StompClient client = StompClient(
config: StompConfig.SockJS(
url: 'https://....../.../mywebsockets',
webSocketConnectHeaders: {
'Upgrade': 'websocket',
'Connection': 'Upgrade',
},
onConnect: (StompFrame connectFrame) {
print('connected');
},
onWebSocketError: (dynamic error) => print(error.toString()),
onStompError: (d) => print("stomp error"),
onDisconnect: (d)=> print("disconnect"),
)
);
At backend(java) we removed .withSockJs() it is not working. Removed handshake still not working.
The problem is that WebSocket source code is actually ditching the WSS scheme and sending an HTTPS request with websocket headers.
uri = Uri(
scheme: uri.isScheme("wss") ? "https" : "http",
userInfo: uri.userInfo,
host: uri.host,
port: uri.port,
path: uri.path,
query: uri.query,
fragment: uri.fragment);
Source: https://github.com/dart-lang/sdk/blob/aa793715e0f122cbd27cd8f9cc12201ff43c19b1/sdk/lib/_http/websocket_impl.dart#L1014 .

How to send cookie in flutter graphql calls

I am working on an Graphql QUERY API which reads domain cookies and based on that return data but when working on flutter app I am not able to send cookies(Which is default in browser). Is there an easier way to send cookies.
Below is my code snippet for Reading cookies from domain. I think there should be some way to set cookies/options in httpLink somehow.
final cookieManager = WebviewCookieManager();
final gotCookies = await cookieManager.getCookies('https://someApp.com');
final HttpLink httpLink = HttpLink(
uri: 'https://someApp.com/graphql',
includeExtensions: true
);
ValueNotifier<GraphQLClient> client = ValueNotifier(
GraphQLClient(
cache: InMemoryCache(),
link: httpLink,
),
);
Found solution for this. We can set cookie in headers just like below and it works.
HttpLink httpLink = HttpLink(
uri: 'https://apps.cloudhealthtech.com/ui-session',
headers: {'Cookie': 'session_id=ctHB0ZewfasKWBWIUu;'});
Found a solution to pass Browser cookies along GraphQL requests without retrieving them first. It uses the Browser client
import 'package:http/browser_client.dart';
Insert a prepared Http client into the httpLink
var myClient = BrowserClient();
myClient.withCredentials =true;
final httpLink = HttpLink(endpoint +"/graphql",
httpClient: myClient,
// ...
);

Cache in GrapghQl Flutter package settings and how to check it works

I'm trying to implement graphQl Flutter package in my app.
https://github.com/zino-app/graphql-flutter
Everything works well, but I have some issues with cache.
If we ran example from this package https://github.com/zino-app/graphql-flutter/tree/master/packages/graphql_flutter/example we can see that cache doesn't work.
In my app I also can't increase speed, it always get data online.
The code from this example
class GraphQLWidgetScreen extends StatelessWidget {
const GraphQLWidgetScreen() : super();
#override
Widget build(BuildContext context) {
final HttpLink httpLink = HttpLink(
uri: 'https://api.github.com/graphql',
);
final AuthLink authLink = AuthLink(
// ignore: undefined_identifier
getToken: () async => 'Bearer $YOUR_TOKEN',
);
Link link = authLink.concat(httpLink);
if (ENABLE_WEBSOCKETS) {
final WebSocketLink websocketLink = WebSocketLink(
url: 'ws://localhost:8080/ws/graphql',
config: SocketClientConfig(
autoReconnect: true, inactivityTimeout: Duration(seconds: 15)),
);
link = link.concat(websocketLink);
}
final ValueNotifier<GraphQLClient> client = ValueNotifier<GraphQLClient>(
GraphQLClient(
cache: OptimisticCache(
dataIdFromObject: typenameDataIdFromObject,
),
link: link,
),
);
return GraphQLProvider(
client: client,
child: const CacheProvider(
child: MyHomePage(title: 'GraphQL Widget'),
),
);
}
}
Animation that shows that cache doesn't work
So, the question is - what is the right way to implement cache and how check it works.
Thank you!
The examples are meant to be standalone - as such they use separate clients and caches. The consequence of this is that it is re-instantiated every mount. In other words, every time you navigates to the example's route, you get a new cache, so you can't see the caching effects. For a more substantial example where you can see if the cache is working, see the starwars example (related github discussion)

Perculiar error when trying to make request to local GQL endpoint

I'm attempting to set up a android application in react-native and it's calling a local API.
Ideally here I'd like the see the API request actually succeed or at least return me a formatted GraphQL exception that should be produced in my back-end.
My GraphQL api is running on localhost:3000
I've already tried the generic solutions for my any question that I've stumbled across
Set up an android emulator HTTP proxy to 10.0.2.2:3000
I've set up the ApolloClient like so
const client = new ApolloClient({
link: new HttpLink({
uri: Platform.select({
android: 'http://"my machine ip here" / ipv4 /graphql'
})
}),
cache: new InMemoryCache(),
});
I've also tried
const client = new ApolloClient({
link: new HttpLink({
uri: Platform.select({
android: 'http://10.0.2.2:3000/graphql'
})
}),
cache: new InMemoryCache(),
});
The API request is made here:
const [login, {data, loading, error}] = useMutation(LoginUserMutation);
return <LoginWrapper>
<Formik
initialValues={{email: '', password: ''}}
onSubmit={async ({email, password}) => {
const user = await login({variables: {email, password}});
}}
>
{({
values,
handleChange,
errors,
setFieldTouched,
touched,
isValid,
handleSubmit
}) => <>
<Input
value={values.email}
onChangeText={handleChange('email')}
onBlur={() => setFieldTouched('email')}
placeholder='E-mail'
/>
<Input
value={values.password}
onChangeText={handleChange('password')}
placeholder='Password'
onBlur={() => setFieldTouched('password')}
secureTextEntry={true}
/>
<Button
onClick={handleSubmit}
text='Sign In'
/>
</>}
</Formik>
</LoginWrapper>
};
The graphql mutation can be seen here
export const LoginUserMutation =
gql(
mutation('loginUser',
params(
{
$args: 'LoginUserInput!'
},
{
updateUser: params({args: '$args'},
{
email: types.string,
}
),
})
)
);
Image of error can be found here -
https://imgur.com/a/6odLPnU
Partial stack trace here -
Possible Unhandled Promise Rejection (id: 0):
Error: Network error: Response not successful: Received status code 400
ApolloError#blob:http://localhost:8081/86efb950-3eff-404f-bc63-41535a310e3a:92518:28
error#blob:http://localhost:8081/86efb950-3eff-404f-bc63-41535a310e3a:93755:30
notifySubscription#blob:http://localhost:8081/86efb950-3eff-404f-bc63-41535a310e3a:141965:20
onNotify#blob:http://localhost:8081/86efb950-3eff-404f-bc63-41535a310e3a:142004:23
So I've solved the problem.
The mutation should have been
export const LoginUserMutation = gql(
mutation('login',
params({$args: 'LoginUserInput!'}, {
login: params({args: '$args'}, {
token: types.string,
}),
})
)
);
The calling of the mutation should have been
const [login, {data, loading, error}] = useMutation(LoginUserMutation);
await login({variables: {args: {email, password}}})