Open rajihawa opened 4 years ago
I am asking my self similar question. in react apollo we can do something like
const link = createHttpLink({
uri: '/graphql',
credentials: 'same-origin' //or credentials: 'include'
});
const client = new ApolloClient({
cache: new InMemoryCache(),
link,
});
I don't know how to do this in this library. @mainawycliffe how do we do this?
@rajihawa have you found solution?
No, but I found a "workaround", I don't know how efficient it is though. using the requests package.
Future<String> getCookie() async {
Map<String, String> cookie = await Requests.getStoredCookies(
Requests.getHostname(uri));
try {
return cookie.keys.first + "=" + cookie.values.first;
} catch (e) {
print(e);
return '';
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final HttpLink httpLink = HttpLink(
uri: uri,
headers: {"cookie": await getCookie()});
ValueNotifier<GraphQLClient> client =
ValueNotifier(GraphQLClient(link: httpLink, cache: InMemoryCache()));
return runApp(MyApp(
client: client,
));
}
@rajihawa thanks for response. Can you also help me how you save cookies to requests library?
@kateile you send a request to an api like that for example
Requests.post("https://api.com/login",
bodyEncoding: RequestBodyEncoding.JSON,
body: {
'email': email,
'password': password
})
and the api is suppose to store the cookie as HttpOnly. and using the methods in the earlier comment I fetched the cookies.
@rajihawa nice trick. But the server I am working with expose only GraphQL api. I think my workaround would be to use requests to send post to graphQL endpoint. Thanks for help
Yes that's how you would do it if your api only expose graphql endpoint, But still this workaround is not that efficient and we nned the solution that flutter_graphql should provide. do you know anyone from the devs that can help us ?
I too have recently run into needing this option available.
Same
is this package still being maintained ??
I have made a new workaround that will make sure the cookies are being sent with every graphql request. by making a custom httpClient and injecting the cookies with every request
GraphqlConfig graphqlConfig = GraphqlConfig();
Future<String> getCookie() async {
Map<String, String> cookie = await Requests.getStoredCookies(
Requests.getHostname("http://10.0.0.8:4000/auth/login"));
try {
return cookie.keys.first + "=" + cookie.values.first;
} catch (e) {
print(e);
return '';
}
}
class ClientWithCookies extends IOClient {
@override
Future<StreamedResponse> send(BaseRequest request) async {
String cookie = await getCookie();
String getCookieString(String _) => cookie;
request.headers.update('cookie', getCookieString);
return super.send(request).then((response) {
return response;
});
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final HttpLink httpLink = HttpLink(
uri: 'http://10.0.0.8:4000/graphql',
httpClient: ClientWithCookies(),
headers: {"cookie": await getCookie()});
ValueNotifier<GraphQLClient> client =
ValueNotifier(GraphQLClient(link: httpLink, cache: InMemoryCache()));
return runApp(MyApp(
client: client,
));
}
just a note: I handle authentication outside of graphql endpoint hope this will help
@rajihawa I have multiple cookies and try to extract them by using
Future<String> getCookie() async {
try {
final hostname = Requests.getHostname(graphqlEndpoint);
final Map<String, String> map = await Requests.getStoredCookies(hostname);
final List<String> array = [];
map.forEach((key, value) => array.add(key + "=" + value));
return array.join('; ');
} catch (e) {
return '';
}
}
final cookie = await getCookie();
print('Cookie: $cookie');
final HttpLink _httpLink = HttpLink(uri: graphqlEndpoint, headers: {'Cookie': cookie});
But I still get 401
. Do I send cookies in correct way? If yes I think I need to set credentials: 'include'
but don't know how!
It seems we really should have first-class support for cookies.
My first inclination is a custom link, but that might be awkward
In #531 @chynamyerz suggests adding it as a flag to HttpLink
:
final HttpLink httpLink = HttpLink(
uri: 'https://api.github.com/graphql',
credentials: 'include' // As it's seen from the apollo-link-http package in React.
);
This is the current workaround I'm using for anyone who is looking for one
// main.dart
import ...
Future<String> getCookie() async {
Map<String, String> cookie =
await Requests.getStoredCookies(Requests.getHostname(k_Backend_Url()));
if (cookie.isEmpty) {
return '';
} else {
return cookie.keys.first + "=" + cookie.values.first + "";
}
}
class ClientWithCookies extends IOClient {
@override
Future<IOStreamedResponse> send(BaseRequest request) async {
String cookie = await getCookie();
String getCookieString(String _) => cookie;
request.headers.update('cookie', getCookieString);
return super.send(request).then((response) {
return response;
});
}
}
Future<ValueNotifier<GraphQLClient>> createClient() async {
final HttpLink httpLink = HttpLink(
uri: k_Backend_Url() + "/graphql",
httpClient: ClientWithCookies(),
headers: {"cookie": await getCookie()});
return ValueNotifier(GraphQLClient(link: httpLink, cache: InMemoryCache()));
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await PushNotificationService().initialise();
ValueNotifier<GraphQLClient> client = await createClient();
bool isAuthed = await UserProvider().autoLogin(client);
runApp(MyApp(isAuthed));
}
class MyApp extends StatelessWidget {
final bool isAuthed;
MyApp(this.isAuthed);
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (_) => UserProvider(),
),
],
child: MyMaterialApp(isAuthed),
);
}
}
class MyMaterialApp extends StatelessWidget {
final bool _isAuthed;
MyMaterialApp(this._isAuthed);
@override
Widget build(BuildContext context) {
Provider.of<UserProvider>(context).info;
return FutureBuilder(
future: createClient(),
builder: (_ctx, AsyncSnapshot<ValueNotifier<GraphQLClient>> e) =>
e.hasData
? GraphQLProvider(
client: e.data,
child: MaterialApp(
title: 'My App',
home: _isAuthed ? HomeScreen() : LoginScreen(),
routes: {
k_LoginScreen_Route: (_) => LoginScreen(),
k_HomeScreen_Route: (_) => HomeScreen()
},
),
)
: MaterialApp(
title: 'My App',
home: LoginScreen(),
),
);
}
}
Hey @rajihawa, this is actually helpful. However, you may have forgotten to set the cookies.
... (update cookie headers)
return super.send(request).then((response) {
// Retrieve cookies from request headers here after updating it
return response;
});
v4 have option for using other links. I use gql_dio_link and with dio you can use
final dio = Dio();
final cookieJar = CookieJar(); //PersistCookieJar(); //todo.
dio.interceptors.add(CookieManager(cookieJar));
final Link _dioLink = DioLink(
graphqlEndpoint,
client: dio,
);
No need for using request package. Lets play with and wait for stable version
v4 have option for using other links. I use gql_dio_link and with dio you can use
final dio = Dio(); final cookieJar = CookieJar(); //PersistCookieJar(); //todo. dio.interceptors.add(CookieManager(cookieJar)); final Link _dioLink = DioLink( graphqlEndpoint, client: dio, );
No need for using request package. Lets play with and wait for stable version
Thanks @kateile!
Thanks @kateile, your solution works well!
But I got this error during mutation call for upload file due to DioLink :
flutter: *** DioError ***:
flutter: uri: http://localhost:4000/graphql
flutter: DioError [DioErrorType.DEFAULT]: Converting object to an encodable object failed: Instance of 'MultipartFile'
#0 _JsonStringifier.writeObject (dart:convert/json.dart:687:7)
#1 _JsonStringifier.writeMap (dart:convert/json.dart:768:7)
#2 _JsonStringifier.writeJsonValue (dart:convert/json.dart:723:21)
#3 _JsonStringifier.writeObject (dart:convert/json.dart:678:9)
#4 _JsonStringifier.writeMap (dart:convert/json.dart:768:7)
#5 _JsonStringifier.writeJsonValue (dart:convert/json.dart:723:21)
#6 _JsonStringifier.writeObject (dart:convert/json.dart:678:9)
#7 _JsonStringStringifier.printOn (dart:convert/json.dart:876:17)
#8 _JsonStringStringifier.stringify (dart:convert/json.dart:861:5)
#9 JsonEncoder.convert (dart:convert/json.dart:261:30)
#10 JsonCodec.encode (dart:convert/json.dart:171:45)
#11 DefaultTransformer.transformRequest (package:dio/src/transformer.dart:62:21)
#12 DioMixin._transformData (package:dio/src/dio.dart:1014:39)
<…>
Here is my graphql client :
final Link link = DioLink(
'${DotEnv().env['BASE_URL']}/graphql',
client: httpService.dio,
);
client = GraphQLClient(
cache: GraphQLCache(),
link: link,
);
Mutation :
const String uploadFile = """
mutation uploadRequestedFile(\$requestedFileId: ID!, \$file: Upload!) {
uploadRequestedFile(requestedFileId: \$requestedFileId, file: \$file) {
id
documentUrl
}
}
""";
final QueryResult result = await graphqlService.client.mutate(
MutationOptions(
document: gql(uploadFile),
variables: {
'file': await imagePickerService.multipartFileFrom(document.file),
'requestedFileId': document.id,
},
),
);
This problem occurs when serializing the body http. Therefore i checked HttpLink source code, and here is how httpLink handles this case :
final httpBody = _encodeAttempter(
request,
(Map body) => json.encode(
body,
toEncodable: (dynamic object) =>
(object is http.MultipartFile) ? null : object.toJson(),
),
)(body);
Are there any updates on this? Just curious.
Has anyone started with this? I have an idea for an API.
@emme1444 there have been no updates, no
v4 have option for using other links. I use gql_dio_link and with dio you can use
final dio = Dio(); final cookieJar = CookieJar(); //PersistCookieJar(); //todo. dio.interceptors.add(CookieManager(cookieJar)); final Link _dioLink = DioLink( graphqlEndpoint, client: dio, );
No need for using request package. Lets play with and wait for stable version
Thanks @kateile . It saved my day 👍
Hi everyone !
The next solution is perfect for mobile solution, bet have you another workaround for a web app?
final dio = Dio();
final cookieJar = CookieJar(); //PersistCookieJar(); //todo.
dio.interceptors.add(CookieManager(cookieJar));
final Link _dioLink = DioLink(
graphqlEndpoint,
client: dio,
);
Because in the web case the Cookie manager does not catch cookies...
Thanks for your help!
Daniel
session cookies are default approach on web. we need this fixed for web.
In the context of Flutter Web, I 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,
// ...
);
v4 have option for using other links. I use gql_dio_link and with dio you can use
final dio = Dio(); final cookieJar = CookieJar(); //PersistCookieJar(); //todo. dio.interceptors.add(CookieManager(cookieJar)); final Link _dioLink = DioLink( graphqlEndpoint, client: dio, );
No need for using request package. Lets play with and wait for stable version
Hey @kateile, not working for me can you help me?
Hi everyone !
The next solution is perfect for mobile solution, bet have you another workaround for a web app?
final dio = Dio(); final cookieJar = CookieJar(); //PersistCookieJar(); //todo. dio.interceptors.add(CookieManager(cookieJar)); final Link _dioLink = DioLink( graphqlEndpoint, client: dio, );
Because in the web case the Cookie manager does not catch cookies...
Thanks for your help!
Daniel
It doesn't work for me can you please help me . Can you please give me full demo ?
is this been resolved?
v4 have option for using other links. I use gql_dio_link and with dio you can use
final dio = Dio(); final cookieJar = CookieJar(); //PersistCookieJar(); //todo. dio.interceptors.add(CookieManager(cookieJar)); final Link _dioLink = DioLink( graphqlEndpoint, client: dio, );
No need for using request package. Lets play with and wait for stable version
This works as expected, but is there any way to persist cookies between reload. PersistCookieJar() just doesn't work?
Would love to see an official solution for this.
how do I add the
credentials: 'include'
to the httpLink ?