Keeping It Simple: Integrate GraphQL and MobX to your Flutter apps
Tags: flutter, dart, mobx, graphql
Published: 2020-11-02 15:21:45

## Introduction

I stumbled upon the following questions and issues on the web when I was trying to do the same thing for my work---Integrate GraphQL and MobX in Flutter:

- [Does anyone use MobX with GraphQL?](https://www.reddit.com/r/FlutterDev/comments/ca5ha5/does_anyone_use_mobx_with_graphql/)
- [Example of Mobx + GraphQL #200](https://github.com/mobxjs/mobx.dart/issues/200)
- [Write doc comments for all public API #308](https://github.com/mobxjs/mobx.dart/issues/308)

I found out... there are not many available resources that demonstrate how to request and send data back to the server, using GraphQL with MobX inside a Flutter app.

So I decided to write a tutorial on this topic with a slight touch of [clean architecture](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html). ✍️

#### Disclaimer

**This is not an introduction** to GraphQL or MobX. If you haven't heard or tried using either one of them, I suggest looking into ResoCoder's [tutorial](https://resocoder.com/2019/12/26/flutter-mobx-tutorial-transparent-reactive-state-management/) on MobX or reading my previous [article](https://codedash.dev/integrating-graphql-in-flutter/) regarding Flutter and GraphQL.

## Project

Your goal is to create a simple Flutter app that retrieves a list of artists based on a search query sent to a Spotify GraphQL server.

<figure>
  <img src="https://user-images.githubusercontent.com/20706361/88471926-fa27f280-cf40-11ea-9e91-5b83ae8161d2.gif" alt="Search Artists via Spotify API"/>
  <figcaption>Search Artists via Spotify API</figcaption>
</figure>

You can download the final project [here](https://github.com/joshuadeguzman/flutter-mobx-graphql).

#### Overview

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595690422/codedash.dev/keeping-it-simple-with-graphql-mobx/architeture_complete_ztjbee.jpg" alt="Overview of the project's structure"/>
  <figcaption>Overview of the project's structure</figcaption>
</figure>

#### Setup the Flutter project

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

a. From your terminal, clone the repository:

```bash
https://github.com/joshuadeguzman/flutter-mobx-graphql-starter
```

b. In the `pubspec.yaml`, add the following dependencies

```yaml
dependencies:
  ...

  mobx: ^1.2.1+1
  flutter_mobx: ^1.1.0+1
  graphql: ^3.0.0
  either_option: ^1.0.6

dev_dependencies:
  ...
  
  mobx_codegen: ^1.1.0+1
  build_runner: ^1.10.0
```

**Few things to note 💡**

- [mobx](https://pub.dev/packages/mobx) is the package the helps you write reactive applications following MobX's principles
- [mobx_codegen](https://pub.dev/packages/mobx_codegen) makes it super easy for you to use `mobx` and writing store files through annotations and code generation
- [either_option](https://pub.dev/packages/either_option) is a small library that allows you to have typed and safe error handling when invoking functions in your Dart code such as a network or API call

c. Retrieve dependencies of the project

```bash
flutter packages get
```

#### Setup GraphQL client

a. In the `main.dart` file, add the following to your `_MyAppState` class

```dart
class _MyAppState extends State<MyApp> {
    HttpLink _httpLink;
    GraphQLClient _client;

    @override
    void initState() {
        _httpLink = HttpLink(
            uri: 'https://spotify-graphql-server.herokuapp.com/graphql',
        );
        _client = GraphQLClient(
            cache: InMemoryCache(),
            link: _httpLink,
        );

        /// Add other initializations here
    }
}
```

b. Pass the `GraphQLClient`'s instance to the data source class

```dart
class _MyAppState extends State<MyApp> {
    HttpLink _httpLink;
    GraphQLClient _client;

    /// Add the following
    ISpotifyApiDataSource _spotifyApiDataSource;
    ISpotifyApiRepository _spotifyApiRepository;

    @override
    void initState() {
        ...

        /// Pass the [GraphQLClient]'s instance
        _spotifyApiDataSource = SpotifyApiDataSource(_client);
        _spotifyApiRepository = SpotifyApiRepository(_spotifyApiDataSource);

        super.initState();
    }
}
```

c. Provide the `_spotifyApiRepository` to the `ArtistsStore`

```dart
Provider(
  create: (_) => ArtistsStore(_spotifyApiRepository),
),
```

In a real app, you shouldn't always be creating the instances of store classes at the top most part of the tree when providing it. Otherwise, they'll get exposed to other unrelated features of the app.

But for simplicitly, let's just leave it here. 🤷‍♂️

## GraphQL Integration

**GraphQL 101**
- In a typical setup of a GraphQL, you only have to connect your app to a single endpoint
- `GraphQLClient` supports several caching mechanisms right out of the box
- Placing the GraphQL client in the data source layer helps you separate the queries from the widget tree

#### Prepare the data source

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595690427/codedash.dev/keeping-it-simple-with-graphql-mobx/architecture_datasource_uaodwa.jpg" alt="GraphQL queries are placed in the data source layer of the app"/>
  <figcaption>GraphQL queries are placed in the data source layer of the app</figcaption>
</figure>

a. In the file `spotify_api_datasource.dart`, create a query to retrieve the artists from the server

```dart
...
@override
Future<List<Artist>> getArtists(String name) async {
  try {
    const String query = r'''
      query getArtists($name: String!) {
          queryArtists(byName: $name) {
              name
              id
              image
              albums {
                id
                name
              }
          }
      }
    ''';

    ...
  }
}
```

This `query` variable is where you specify the data that you need to pass or retrieve data from the server.

Similarly, this is how you use the `query` in a GraphQL playground.

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595666348/codedash.dev/keeping-it-simple-with-graphql-mobx/graphql_thewgd.gif" alt="Spotify GraphQL API"/>
  <figcaption>https://spotify-graphql-server.herokuapp.com/</figcaption>
</figure>

b. Add the following `QueryOptions`, and assign the `query` variable to the `gql`

```dart
final response = await _client.query(
  QueryOptions(
    documentNode: gql(query),
    variables: {
      'name': name, /// Search input of the user
    },
  ),
);
```

`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).

#### Prepare the repository

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595690422/codedash.dev/keeping-it-simple-with-graphql-mobx/architecture_repository_tr1xvg.jpg" alt="A single repository can retrieve data from one or multiple data sources, eg. local or remote"/>
  <figcaption>A single repository can retrieve data from one or multiple data sources, eg. local or remote</figcaption>
</figure>

a. In the file `spotify_api_repository.dart`, call the `getArtists` from the data source

```dart
@override
Future<Either<Failure, List<Artist>>> getArtists(String name) async {
  try {
    /// Add the following
    final data = await dataSource.getArtists(name);
    return Right(data);
  } ... catch (e) {
    return Left(UnhandledFailure());
  }
}
```

In the `getArtists` function, you are trying to catch the `Exception`s returned after you query the data source.

```dart
try {
  ...
} on OperationException catch (e) {
  /// Handle exceptions that you receive from the [GraphQLClient]
  return Left(OperationFailure(e.graphqlErrors.first.message));
} on NoResultsFoundException {
  return Left(NoResultsFoundFailure());
} on Exception {
  return Left(ServerFailure());
} catch (e) {
  return Left(UnhandledFailure());
}
```

This is where the package [either_option](https://pub.dev/packages/either_option) comes in handy. `Either` is a result type that represents a value of one of the two possible types you assigned to it.

In this project, the function is returning _either_ a:

- type `Failure`, which is a custom class that swallows an `Exception` with a custom message
- or a `List<Artist>` which is the result that you need to populate the artists' list

#### MobX Integration

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595688196/codedash.dev/keeping-it-simple-with-graphql-mobx/triad_gdurvt.png" alt="MobX triad"/>
  <figcaption>Credits: https://pub.dev/packages/mobx</figcaption>
</figure>

**MobX in a nutshell**
- **observables** are the *notifiers* to your UI observing the changes in the values
- **actions** are simply *functions* where you can to change the values of your *observables*
- **reactions** are *listeners* where you handle the side-effects coming from your store---typically used when you need to process something outside the `Observer` widget

#### Prepare the store class

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595690422/codedash.dev/keeping-it-simple-with-graphql-mobx/architecture_store_p5zyhw.jpg" alt="A store can call functions from one or more repositories"/>
  <figcaption>A store can call functions from one or more repositories</figcaption>
</figure>

a. Add a function to retrieve artists from the repository and annotate it with `@action`

```dart
@action
searchArtists(String name) async {
  _artistsFuture = ObservableFuture(_repository.getArtists(name));

  final either = await _artistsFuture;

  artistsResult = either.fold(
    (left) {
      /// [Failure]s are handled here
      this.reset();
      errorMessage = left.message;
      return null;
    },
    (right) {
      /// Results are handled here,
      /// in this case it is of type [List<Artist>]
      return right;
    },
  );
}
```

b. Generate the store file

Run the following command to generate a store file

```bash
flutter pub run build_runner build
```

If you have encountered issues regarding conflicting files, just run

```bash
flutter pub run build_runner build --delete-conflicting-outputs
```

c. In the `artists.screen.dart`, call the function `searchArtists`

```dart
class _ArtistsScreenState extends State<ArtistsScreen> {
  ...

  @override
  void didChangeDependencies() {
    ...
    
    _store.searchArtists(_name);
  }
}
```

#### Observing values in the UI

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595690422/codedash.dev/keeping-it-simple-with-graphql-mobx/architeture_complete_ztjbee.jpg" alt="Final step in integrating GraphQL and MobX is to observe changes in the UI"/>
  <figcaption>Final step in integrating GraphQL and MobX is to observe changes in the UI</figcaption>
</figure>

a. Load the `artistsResult` to the `ArtistList` widget

```dart
Observer(
  builder: (_) {
    switch (_store.state) {
      case StoreState.loading:
        ...
        break;

      /// Add the following widgets to display the artists
      default:
        return Expanded(
          child: SingleChildScrollView(
            child: Container(
              margin: const EdgeInsets.symmetric(horizontal: 20),
              child: ArtistsList(
                artists: _store.artistsResult ?? [],
              ),
            ),
          ),
        );
    }
  },
),
```

**Few things to note 💡**

- `Observer` rebuilds itself whenever there are changes to the value/s it _observes_ inside its `build` method
- In this `Observer`, it rebuilds whenever `_store.state` or `_store.artistsResult`'s value is changed
- Introduce an `Observer` **only** to the widget that needs to rebuild whenever data changes


## Unit testing

This article won't be complete without adding any unit tests!

One of the benefits of separating business logic from the UI is that you get to test a small chunk of the code that makes your whole app.

Here's a snippet from the store unit tests

```dart
test(
  "should SET the [artistsResult] when the repository returns the results",
  () {
    // Arrange
    final name = "Carpenters";
    final result = [
      Artist(name: "The Carpenters"),
      Artist(name: "Carpenters"),
    ];
    when(_repository.getArtists(name)).thenAnswer(
      (_) async => Right(result),
    );

    // Act
    _store.searchArtists(name);

    // Assert
    expect(_store.artistsResult, result);
    expect(_store.artistsResult.length, 2);
    expect(_store.artistsResult.first.name, result.first.name);
  },
);
```

And a snippet from the repository unit tests

```dart
test(
    "should RETURN [List<Artist>] when the data source returns the results from the server",
    () async {
  // Arrange
  final name = "Carpenters";
  final result = [
    Artist(name: "The Carpenters"),
    Artist(name: "Carpenters"),
  ];
  when(_dataSource.getArtists(name)).thenAnswer((_) async => result);

  // Act
  final response = await _repository.getArtists(name);

  // Assert
  expect(response, isA<Right<Failure, List<Artist>>>());
  expect(response.isLeft, false);
  expect(response.isRight, true);
});
```

You can view all the unit tests included in this project [here](https://github.com/joshuadeguzman/flutter-mobx-graphql/tree/master/test).

Run the tests

```bash
flutter test
```

## Conclusion

For brevity, I focused on the missing parts of the app that matter when integrating GraphQL and MobX to your Flutter apps. You can always read the comments I left on the [source code](https://github.com/joshuadeguzman/flutter-mobx-graphql) for more details.

In contrast to the use of `Mutation` and `Query` widget builders in the widget tree using [graphql_flutter](https://pub.dev/packages/graphql_flutter), this project setup makes your app less dependent on using `GraphQL`'s implementation.

If you are planning to include data sources from traditional SOAP or RESTful APIs in the future, you only have to implement it differently on the data sources layer and continue what you are currently doing with the other parts of your app.

<figure>
  <img src="https://res.cloudinary.com/joshuadeguzman/image/upload/v1595739787/codedash.dev/keeping-it-simple-with-graphql-mobx/datasource_x4g3xx.jpg" alt="Connecting to multiple remote data sources"/>
  <figcaption>Connecting to multiple remote data sources</figcaption>
</figure>

That's it for now, see you on the next one!

<!-- # What's next?

Of course, clean and safe code always comes with a price of adding tests. 🤓

If you are interested, I've written an article on how you can add unit tests for this project, you can read more about it [here](http://codedash.dev/keeping-it-safe-adding-unit-test-to-flutter-app-with-mobx/). -->

## References
- [https://pub.dev/packages/mobx](https://pub.dev/packages/mobx)
- [https://resocoder.com/2019/12/26/flutter-mobx-tutorial-transparent-reactive-state-management/](https://resocoder.com/2019/12/26/flutter-mobx-tutorial-transparent-reactive-state-management/)
- [https://codedash.dev/integrating-graphql-in-flutter](https://codedash.dev/integrating-graphql-in-flutter)

[This](https://blog.joshuadeguzman.net/keeping-it-simple-integrate-graphql-mobx-to-your-flutter-app) was originally posted on my [blog](https://blog.joshuadeguzman.net). Say hi! 👋