Graphql is a query language for APIs that gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. It uses one endpoint, and users can structure their query such that they only fetch exactly what they need at a time.
In this article, I have built a mobile app that queries a graphql API and returns a list of continents and countries in the world. You can find the API here => countries.trevorblades.com.
For this project, we will utilize the following packages in our app
graphql_flutter
provider
Create a new flutter project and add these packages to your pubspec.yaml file in the root directory.
Next, create the following folders/files in your lib folder; Models, Queries, Services, UI, Utils, config.dart. Your lib folder should look like this;
Lib
- Models
- Queries
- Services
- UI
- Utils
- config.dart
- main.dart
Set-up graphql
The next step after creating your folders is to set up graphql, open your config.dart, and paste the following code
import 'package:countries_graphql_tutorial/Models/continents_model.dart';
import 'package:countries_graphql_tutorial/Queries/queries.dart';
import 'package:countries_graphql_tutorial/config.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
class ApiService {
GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration();
AllQueries allQueries = AllQueries();
Future<Data> getContinents() async {
try {
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client
.query(QueryOptions(documentNode: gql(allQueries.continentsQuery())));
if (result.hasException) {
print(result.exception.toString());
return null;
}
var response = result.data.data;
final map = response as Map<String, dynamic>;
Data mappedResult = Data.fromJson(map);
return mappedResult;
} catch (e) {
print(e);
return null;
}
}
}
Our single endpoint/uri for the project is ‘trevorblades.com’, as I said earlier graphql has only one endpoint where all requests are made, unlike rest API which utilizes different uri for different calls.
GraphqlConfiguration class is where our graphql connection is being made, and it has a single method, clientToQuery().
Writing your queries
Next is to write your graphql query, if you’re just starting with graphql, you can visit their official site to learn how to write queries and mutations graphql.org.
The query for this project should be in your Queries folder, create a file called queries.dart, and paste the following code.
class AllQueries {
String continentsQuery() {
return """
query Continent{
continents{
code
name
countries{
code
name
phone
capital
currency
languages{
name
}
states{
name
}
}
}
}
""";
}
}
This is the only query in our app, and it fetches all the continents as well as the countries under them.
Model class
After writing your query, paste it on the playground — countries.trevorblades.com and run.
You should get a response on the second panel. Create a JSON to dart model class manually from the response under Models/continent_models.dart or use app.quicktype.io. It automatically creates a model class for you in different languages, paste the query response, select dart and copy the result.
Stringing it all together, Service class
Now that you have your model and query file ready, you can now write your service class. Create a file under services called continent_service.dart and paste the following code.
import 'package:countries_graphql_tutorial/Models/continents_model.dart';
import 'package:countries_graphql_tutorial/Queries/queries.dart';
import 'package:countries_graphql_tutorial/config.dart';
import 'package:graphql_flutter/graphql_flutter.dart';
class ApiService {
GraphQLConfiguration graphQLConfiguration = GraphQLConfiguration();
AllQueries allQueries = AllQueries();
Future<Data> getContinents() async {
try {
GraphQLClient _client = graphQLConfiguration.clientToQuery();
QueryResult result = await _client
.query(QueryOptions(documentNode: gql(allQueries.continentsQuery())));
if (result.hasException) {
print(result.exception.toString());
return null;
}
var response = result.data.data;
final map = response as Map<String, dynamic>;
Data mappedResult = Data.fromJson(map);
return mappedResult;
} catch (e) {
print(e);
return null;
}
}
}
The class name is ApiService, and it creates a Graphql client using the clientToQuery method in the GraphQLConfiguration class. Gql is used to parse GraphQL query strings into the standard GraphQL AST. The response returned is a map and your model is mapped from it.
UI FOLDER
Lastly, we can get to work on our UI, as most of the work is out of the way. I use a future provider to call the endpoint to the UI.
import 'package:flutter/material.dart';
import 'Models/continents_model.dart';
import 'Services/continent_service.dart';
import 'UI/home.dart';
import 'package:provider/provider.dart';
void main() {
runApp(App());
}
class App extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
FutureProvider<Data>(
create: (_) {
return ApiService().getContinents();
},
),
],
child: MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
debugShowCheckedModeBanner: false,
home: HomeView(),
),
);
}
}
The other views include home.dart where the continents in the world are displayed and countries.dart where countries under each continent are displayed. You can find their files here.
And that’s the end! we have a working app that should look like this.
If your app doesn’t look the same, don’t forget to check out the Github repository for the project files to see where you might have gone wrong.
Thank you for taking your time to read it, please feel free to leave comments/questions if any.