Integrating GraphQL in Flutter using graphql_flutter
Tags: flutter, dart, graphql, tutorial
Published: 2020-11-02 15:19:25

## Introduction

Ever wonder how the big guys like Facebook, Twitter, Airbnb create robust frontend applications for their platforms? Well, one of the key ingredients in their success is the use of GraphQL.

But, what exactly is GraphQL?

GraphQL is a query language---surprisingly, that’s what the last two letters represent.

It’s a query language for your API. It enables the frontend app (a.k.a. client) to request the exact data they need from the server, whether for a specific page, component, or just whenever they need it.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/uuf77o2zk9ujmo7m2wlf.gif" alt="GraphQL sample query"/>
  <small>Sample GraphQL query requesting for upcoming launch data</small>
</figure>

Technically speaking, it's just a [specification](https://spec.graphql.org/June2018/)---**not an implementation**. For you to be able to write and host GraphQL APIs, you need a server library that contains the implementation written for the platform of your choice---one of my favorites is the [Apollo Server](https://www.apollographql.com/docs/apollo-server/v1/) for NodeJS.

And for you to be able to consume these GraphQL APIs, you need to use one of the client-side implementations, such as the [graphql_flutter](https://pub.dev/packages/graphql_flutter).

You can find an exhaustive list of supported server and client libraries for GraphQL [here](https://graphql.org/code/).

**Some benefits for frontend developers**:

- You only have to connect your app to a single endpoint
- You don't necessarily have to deal with multiple API/endpoint chainings yourself
- Multiple caching mechanisms supported right out of the box

Most of the tedious work is likely to be handled in the backend---but hey, they also get extra time to rest because you don't always have to ping them anymore whenever you need something changed ASAP.

## Project

In this article, I will teach you how you can request or send data to a GraphQL API from a Flutter application.

Download the final project [here](https://github.com/joshuadeguzman/spacex-land).

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/rh34wp9tlca3sgxyefcn.gif" alt="Project Overview"/>
  <figcaption>Flutter application utilizing SpaceX GraphQL API</figcaption>
</figure>

#### Setup the Flutter project

At the time of writing, I am using the Flutter SDK stable v1.17.5.

From your terminal, clone the repository:

```bash
https://github.com/joshuadeguzman/spacex-land-starter
```

In the `pubspec.yaml`, add the dependency `graphql_flutter`

```yaml
dependencies:
  flutter:
    sdk: flutter

  cupertino_icons: ^0.1.3
  google_fonts: ^1.1.0
  intl: ^0.16.1
  cached_network_image: ^2.2.0+1
  graphql_flutter: ^3.0.0 # Add it here
```

Retrieve dependencies of the project

```bash
flutter packages get
```

#### Setup GraphQL client

In the `main.dart` file, add the following to your `main()` function

```dart
void main() {
  WidgetsFlutterBinding.ensureInitialized();

  final HttpLink link = HttpLink(
    uri: 'https://api.spacex.land/graphql/',
  );

  ValueNotifier<GraphQLClient> client = ValueNotifier(
    GraphQLClient(
      cache: InMemoryCache(),
      link: link,
    ),
  );

  runApp(MyApp(client: client));
}
```

As mentioned earlier, you only have to establish a connection to a single endpoint. In the project, you are only querying anonymously on this API---no authentication tokens or cookies are involved.

To finish the setup, you need to provide the GraphQL instance throughout the app by wrapping the `MaterialApp` with `GraphQLProvider`.

```dart
class MyApp extends StatelessWidget {
  final ValueNotifier<GraphQLClient> client;

  const MyApp({Key key, this.client}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final textTheme = Theme.of(context).textTheme;

    return GraphQLProvider(
      client: client,
      child: CacheProvider(
        child: MaterialApp(
          ...
          home: HomeScreen(),
        ),
      ),
    );
  }
}
```

Let's get going and start querying the API.

## Queries

In GraphQL, a `query` allows you to explicitly tell the API that you are only requesting data from the server. In a traditional RESTful API, this is equivalent to a `GET` method request.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/vrlvq8g07wsl5rjjkm6e.gif" alt="Querying the launch history from the API"/>
  <figcaption>Querying the launch history from the API</figcaption>
</figure>

#### Fetching the data from the server

Your first goal is to retrieve the users from the server, and display it on a list.

a. In the `users.page.dart`, import the `graphql_flutter` dependency

```dart
import 'package:graphql_flutter/graphql_flutter.dart
```

b. Remove the `Center` widget and uncomment the `Query` widget

```dart
class _UsersPageState extends State<UsersPage> {
  @override
  Widget build(BuildContext context) {
    // Remove this widget
    return Center();

    // Uncomment the entire [Query] widget
    return Query(
       ...
    );
  }
}
```

c. Add the users query

```dart
class _UsersPageState extends State<UsersPage> {
  final String _query = """
    query users {
      users {
        id
        name
        rocket
        twitter
      }
    }
  """;

  @override
  Widget build(BuildContext context) {
    ...
  }
}
```

As you can imagine, this is the exact query you are using in the GraphQL playground to get all the users from the server.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/ds05479gf0tcp7rqlja0.png" alt="https://api.spacex.land/graphql/"/>
  <figcaption>https://api.spacex.land/graphql/</figcaption>
</figure>

d. Next, add `gql(_query)` to the `documentNode` of `QueryOptions`

```dart
Query(
    options: QueryOptions(
      documentNode: gql(_query),
    ),
    builder: (
      QueryResult result, {
      VoidCallback refetch,
      FetchMore fetchMore,
    })
    ...
)
```

`gql` turns your `String` query into a standard GraphQL AST (a.k.a. Abstract Syntax Tree). To put it simply, Stephen Schneider once mentioned that AST is just a `fancy way of saying heavily nested objects` from his [video](https://www.youtube.com/watch?v=sbOe6fU0SCI).

e. Hit hot restart or press `SHIFT` + `R` in your terminal

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/0ce7o1mi8bimuaaqgue4.gif" alt="Astronaut's landing page (pun intended)"/>
  <figcaption>Astronaut's landing page (pun intended)</figcaption>
</figure>

**Everything else is self-descriptive, but what is happening here?**

1. After you finished querying the server, you parse the data using `final List users = result.data["users"];`
2. Next, you retrieve each user from the list using `final user = users[index];` inside a `ListView.builder`
3. Lastly, you retrieve the data from the `user`, i.e. `user['name']`, and display it in a `Text` widget

For a more structured way on how to parse JSON objects, please read more about it [here](https://flutter.dev/docs/development/data-and-backend/json).

_Optional_: If for some reason you need to display the `timestamp` that indicates when the user was created, add `timestamp` in the `String` query.

```dart
final String _query = """
  query users {
    users {
      id
      name
      rocket
      twitter
      timestamp
    }
  }
""";
```

And add a widget to display the timestamp data, `user['timestamp']`, in the list.

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/34u8lhf82n8ra0nowtty.gif" alt="Displaying timestamp in the list"/>
  <figcaption>Displaying timestamp in the list</figcaption>
</figure>

Phew, that went easy.

I challenge you to do the same for the upcoming launches and launch history screens. I'll leave you with the queries I used for the final project.

**Upcoming launches query**

```dart
/// lib/screens/home/launch_upcoming.page.dart

class LaunchUpcomingPage extends StatelessWidget {
  final DateFormat _dateFormat = DateFormat("MMMM dd, yyyy");

  final String _query = """
    query launchUpcoming {
        launchesUpcoming(limit: 10) {
          mission_name
          launch_date_utc
          rocket {
            rocket_name
            rocket_type
          }
          links {
            flickr_images
          }
        }
      }
  """;
}
```

**Launch history query**

```dart
/// lib/screens/home/launch_history.page.dart

class LaunchHistoryPage extends StatelessWidget {
  final DateFormat _dateFormat = DateFormat("MMMM dd, yyyy");

  final String _query = """
    query launchHistory {
        launchesPast(limit: 10) {
          mission_name
          launch_date_utc
          rocket {
            rocket_name
            rocket_type
          }
          links {
            flickr_images
          }
        }
      }
  """;
}
```

You might have noticed the `limit` parameter in the query, this is just simply to tell the server that you only need the first 10 items in the database. I'll discuss GraphQL pagination on a separate article.

## Mutations

A `mutation` is used when you want to modify a single or set of data on the server. You might have guessed it, using a `mutation` is the equivalent of using `POST`, `PATCH`, or `PUT`, `DELETE` methods in a traditional RESTful API.

#### Creating a new user

a. Inside the `add_user.screen.dart`, wrap the `Form` widget inside a `Mutation` widget

<figure>
  <img src="https://dev-to-uploads.s3.amazonaws.com/i/sg85fzurxzokmxmsg75d.gif" alt="Wrapping Form in a Mutation widget"/>
  <figcaption>Simplest hack to wrap your Form inside a Mutation widget, this imports `graphql_flutter` as well</figcaption>
</figure>

b. Add the following mutation to create a new user in the server

```dart
class _AddUserScreenState extends State<AddUserScreen> {
  ...

  String insertUser() {
    return """
      mutation insertUser(\$name: String!, \$rocket: String!, \$twitter: String!) {
        insert_users(objects: {
          name: \$name,
          rocket: \$rocket,
          twitter: \$twitter,
        }) {
          returning {
            id
            name
            twitter
            rocket
          }
        }
      }
    """;
  }

  ...
}
```

Notice how parameters are escaped in this query, these are examples of GraphQL [variables](https://graphql.org/learn/queries/#variables).

c. Update the `Mutation`'s `options` and `builder` respectively

```dart
Mutation(
  options: MutationOptions(
    /// Insert mutation here
    documentNode: gql(insertUser()),

    /// Tell the GraphQL client to fetch the data from
    /// the network only and don't cache it
    fetchPolicy: FetchPolicy.noCache,

    /// Whenever the [Form] closes, this tells the previous [route]
    /// whether it needs to rebuild itself or not
    onCompleted: (data) => Navigator.pop(context, data != null),
  ),
  builder: (
    RunMutation runMutation,
    QueryResult result,
  ) {
    ...
  }
)
```

d. Look for the `FlatButton`, and add the following to its `onPressed` callback

```dart
if (_formKey.currentState.validate()) {
  ...

  runMutation({
    'name': _nameController.text,
    'rocket': _rocketNameController.text,
    'twitter': _twitterController.text,
  });
}
```

With `runMutation`, you can tell the GraphQL client when exactly do you need to modify a data in the server. You can call this function within the scope of the `Mutation` builder.

In addition, this is where you pass the data to the variables from the `insertUser()` mutation.

e. Hit hot restart or press `SHIFT` + `R` in your terminal

<figure>
  <table>
    <tr>
      <td width="25%">
        <img src="https://dev-to-uploads.s3.amazonaws.com/i/salx79htki2crvxqpqa9.gif" style="height: 550px;" alt="Adding new user"/>
      </td>
      <td>
        <img src="https://dev-to-uploads.s3.amazonaws.com/i/zeczyfs0dmw4fftdiyib.png" alt="Retrieving list of users"/>
      </td>
    </tr>
  </table>
  <figcaption>Adding @JeffBezos with his heavy-lift orbital launch vehicle</figcaption>
</figure>

Awesome!

Now, I'll challenge you again to add functionality to update the details of the users on the server.

**Update user details query**

```dart
class _UpdateUserScreenState extends State<UpdateUserScreen> {
/// lib/screens/update_user/update_user.screen.dart

  ...

  String _updateUser() {
    return """
    mutation updateUser(\$id: uuid!, \$name: String!, \$rocket: String!, \$twitter: String!) {
      update_users(_set: {name: \$name, rocket: \$rocket, twitter: \$twitter}, where: {id: {_eq: \$id}}) {
        returning {
          id
          name
          twitter
          rocket
        }
      }
    }
    """;
  }
}
```

## Conclusion

GraphQL makes it easy for frontend developers to play around with the server-side data and just focus on building the features. I encourage you to watch the [documentary](https://www.youtube.com/watch?v=783ccP__No8) on how Facebook engineers invented GraphQL to help solve their issues on building native apps for the platform.

**Q: Is GraphQL going to replace traditional RESTful APIs?** No. At least not in the near future. There are still some cases where RESTful API may be a better choice over GraphQL. And just like any other solutions in software development, there's no silver bullet that will solve every business' problems when it comes to serving APIs.

## What's next?

I have shared with you one of the simplest ways on how you can request and send data back to the server using [graphql_flutter](https://pub.dev/packages/graphql_flutter). But you'll probably look for a better way of implementing this on your apps if scalability and maintainability are some of your major concerns.

For example, when working with large teams, you may need to have typesafe queries and mutations, auto-generated classes, tests and more. Well, I’ll show them to you in action next time. 👋

## References

- [https://pub.dev/packages/graphql_flutter](https://pub.dev/packages/graphql_flutter)
- [https://graphql.org/learn/](https://graphql.org/learn/)
- [https://flutter.dev/docs/development/data-and-backend/json](https://flutter.dev/docs/development/data-and-backend/json)

[This](https://blog.joshuadeguzman.net/integrating-graphql-in-flutter) was originally posted on my [blog](https://blog.joshuadeguzman.net). Say hi! 👋