Flutter GraphQL: Quick start with Fauna
Tags: flutter, graphql, fauna, mobile
Published: 2021-05-07 03:51:13

# Flutter GraphQL: Quick start

**Flutter** is a UI toolkit for building mobile, web and desktop applications from a single codebase.

**GraphQL** is a powerful query language designed to make APIs fast, flexible, and developer-friendly.

**Fauna** makes it easy to create a GraphQL API backed by a scalable and secure transactional database.

In this series of articles, we’ll cover how to build a Flutter application powered by a Fauna GraphQL API.

Part 1: Quick start

In this article:

- Why GraphQL with Flutter?
- Why Fauna with Flutter?
- Create a GraphQL API in minutes
- Create data with a GraphQL mutation
- Find a single item with a GraphQL query
- Fetch multiple items with a GraphQL query
- Set up a GraphQL client in Flutter
- Populate a Flutter ListView with a GraphQL query

## Why GraphQL with Flutter?

I do not intend to write another GraphQL vs. REST comparison article, but it is beneficial to understand the benefits you should expect from using GraphQL in Flutter apps. Maintaining a clear understanding of what works well with GraphQL is essential to getting the most out of it.

### Optimal data size

When you fetch data with GraphQL, you specify exactly the structure you need for each query. If your screen only needs a few properties, you can greatly reduce bandwidth and latency to optimize the end user experience.

### Data aggregation

Many applications need to fetch many different types of resources to display useful information on a screen. For example, in a screen that displays a list of spaceships in your galactic starfleet, you may need to display information about each ship, the ship’s crew, the ship’s passengers, the planets where they are docked, etc. GraphQL allows you to specify what you need from each resource in a single query and takes care of the complexity of aggregation for you.

### Intelligent caching

In GraphQL, every resource is globally identifiable with a unique identifier and type classification. Client libraries can use this information to intelligently cache objects for later use. Two separate queries for the same data? No problem, the second query can be resolved straight from the cache without writing any additional code to manage that state.

### Development flexibility

Because clients specify the exact data needed in each query, frontend developers can easily add or remove properties from the query to fetch the data required. This means you don’t need to make changes to your backend API (or wait for somebody else to do it) if the display requirements of your screens change.

## Why Fauna GraphQL with Flutter?

Many concepts in this series will work for any GraphQL backend, but as you follow along, I hope you see the benefits of using Fauna with your Flutter apps.

### Easy to get started

You can create a globally distributed, failsafe, secure, and scalable GraphQL API up and running in just a few minutes.

### Productive data modeling

Fauna supports documents, relations, and graphs providing great data modeling flexibility that can be easily enhanced as your application evolves.

### Zero infrastructure management

Fauna provides an API, not just a database, so you don’t need to manage any infrastructure.

### Power when you need it

Fauna’s auto generated GraphQL API will help you get started. As your needs evolve, you can add custom queries and mutations with all the power of [FQL](https://docs.fauna.com/fauna/current/api/fql/), Fauna’s comprehensive query language.

## Flutter + GraphQL Course

We are working on a comprehensive **online course** for Flutter developers who want to build amazing Flutter apps with GraphQL.

Whether you have worked with Flutter and GraphQL before or are just getting started, there will be something in this course for you.

Learn more at [GraphQL for Flutter Developers](/flutter-graphql).

## Create a Fauna GraphQL API in minutes

In the first section of this tutorial, we will create a new GraphQL API for Spaceships Flutter, an app that helps us manage a fleet of galactic spaceships.

### Create a GraphQL schema definition file

First, copy this text into a file called `schema-definition.gql`.

```graphql
type Spaceship {
  name: String!
}
```

We refer to this file as a _schema definition_ file because it provides the minimum set of information that allows Fauna to create collections, indexes, etc. and then make those available via GraphQL API.

The `Spaceship` type above defines our first resource type. For now, our spaceships only need a name, which is a string. The exclamation point on `String!` means this field is a required, non-nullable field.

## Import the GraphQL schema definition file into Fauna

1. If you haven’t already, [register an account with Fauna](https://dashboard.fauna.com/accounts/register?utm_source=DevTo&utm_medium=referral&utm_campaign=WritewithFauna_GraphQLFlutter_SConnolly).
2. From the Fauna dashboard, select **New Database**.
   ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1lw5xmq21qzf561u6vam.png)
3. Enter **flutter_spaceships** for the name and select **Save**.
   ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/k64g5yxdiuak901h2rf2.png)
4. In the left navigation, select **GraphQL**.
   ![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3e3l2p0q2cqowh0fvshg.png)
5. Select **Import Schema** and upload the `schema-definition.gql` file we created earlier.
6. When the upload is complete, you should land in the **GraphQL Playground**, which means you are ready to move on to the next step.

## Explore the GraphQL API

The GraphQL API is now available for us to start using, but what exactly did Fauna just do with that uploaded file?

Since we are here in the GraphQL Playground, let’s view the docs by selecting the **Docs** tab.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2upod7bgg7h4m4asrp82.png)

This view informs us of all of the operations available in the GraphQL API. The first section lists the available queries, operations that allow us to read data from our database. The next section lists mutations, operations that allow us to change the data in our database through creations, updates, and deletions.

These are all standard CRUD (Create - Read - Update - Delete) operations that Fauna implements for us based on the definition of our schema that we provided in the prior step.

All of this was generated by Fauna simply by defining the **Spaceship** type:

```graphql
type Spaceship {
  name: String!
}
```

This single type definition generated 4 operations:

- `findSpaceshipById`
- `createSpaceship`
- `updateSpaceship`
- `deleteSpaceship`

Now that we’ve explored the GraphQL API, let’s start using it to build our space fleet.

## Create data with GraphQL mutations

Our fleet won’t be much of a fleet without some spaceships. Eventually we will be able to add spaceships from our Flutter app, but for now we will continue to use Fauna’s GraphQL Playground to write and execute mutations.

Since we are _adding_ a spaceship, we need to _create_ data. Let’s inspect the `createSpaceship` mutation.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/usnp6gouhsu7e83dfw91.png)

We can see that the `createSpaceship` mutation accepts a single parameter called `data`, which is of type `SpaceshipInput`. `SpaceshipInput` includes a `name` property, which is the exact property we specified when we defined the `Spaceship` type in our original schema definition.

With that, we can enter this mutation into the input of the **GraphQL Playground**.

```graphql
mutation {
  createSpaceship(data: { name: "Serenity" }) {
    _id
    _ts
    name
  }
}
```

Since the `createSpaceship` mutation returns a `Spaceship`, we can specify the information about the created spaceship that we want to retrieve after the mutation completes. `_id` and `_ts` are properties that Fauna adds to every type in its database; they capture the document’s unique identifier and creation timestamp respectively.

And as soon as we execute the mutation, we have a spaceship in our system.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b234yi0la9kj2fwyrmop.png)

Feel free to add a few more spaceships to your fleet. We will fetch and display these in a Flutter ListView later.

## Find a single item with a GraphQL query

Now that there is a spaceship in our system, how do we find it again later?

If we inspect the docs in the GraphQL Playground again, we can see the `findSpaceshipById` query is just what we need. We can also see that this query takes only one [argument](https://graphql.org/learn/queries/), the `id` of the spaceship we want to find.

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0gc6mzvzkrkw63k5kyj8.png)

In the prior mutation we requested the spaceship’s `_id` in the sub selection, so let’s copy that `_id` into the `findSpaceshipById` query to demonstrate how we can easily lookup a document with its unique identifier.

```graphql
query {
  findSpaceshipByID(id: "enter-id-here") {
    _id
    name
  }
}
```

Replace the `id` value with the `_id` from the prior mutation response and execute the query in the GraphQL Playground. You should see a result like this:

![](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rco2emh9ei4flqj97q0r.png)

## Fetch multiple items with a GraphQL query

In our Spaceships Flutter app, we are not going to ask users to enter spaceship ids in order to view their fleet. Instead, we will want to display a list of spaceships in the fleet. Currently we can only get a single spaceship if we know its unique id, so how do we get a list of spaceships?

First we need to update our GraphQL schema by adding another type to the `schema-definition.gql` file we uploaded earlier.

```graphql
type Spaceship {
  name: String!
}

type Query {
  spaceships: [Spaceship!]!
}
```

The `Query` type is a special type in GraphQL. It defines all of the top-level entry points that are available for clients to execute queries against. Here we are adding a `spaceships` query that will return a list of Spaceship items. The square brackets around `[Spaceship!]!` indicate this return type is a [GraphQL array](https://graphql.org/learn/schema/#object-types-and-fields).

In the GraphQL Playground, there is an **Update Schema** action that will prompt you to upload the latest schema definition. Select `schema-definition.gql` again to update the schema.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n5bzibl3j3xowptx3gsl.png)

After the upload is complete, inspect the docs again to see that a new `spaceships` query was added.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jyk548bedc6wvnefxq8c.png)

Notably, the final schema generated by Fauna is not exactly as it was defined. Instead of returning an array like we defined, the `spaceships` query returns a `SpaceshipPage!`. We can also see that the `spaceships` query accepts two inputs: `_size` and `_cursor`.

The return type and the additional inputs are here to allow us (well actually _force_ us) to use pagination when fetching lists of items. Data sets in any system can grow very large and limiting the amount of data fetched in each query helps optimize server resources, latency, and bandwidth.

If we don’t specify a `_size`, Fauna will use its default limit of 64, which is fine for us for now since our fleet hasn’t exceeded that yet.

Go ahead and execute this query in the **GraphQL Playground**.

```graphql
query {
  spaceships {
    data {
      _id
      name
    }
  }
}
```

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nycq9qbbe9bd66dapxiy.png)

Our GraphQL API is ready and has what we need to start building our Flutter app.

## Set up a GraphQL client in Flutter

In this section we are going to create a new Flutter project and use the [Ferry](https://pub.dev/packages/ferry) GraphQL client to connect to our GraphQL API. There are a few other Flutter GraphQL clients available on [pub.dev](https://pub.dev/) but I believe Ferry is the best option available and is easy to get started with.

If you haven’t worked with Flutter before, follow the [Get started](https://flutter.dev/docs/get-started/install) steps.

First, create a new Flutter project by running this command in your terminal:

```bash
flutter create flutter_spaceships
```

Next, we need to add a few dependencies to our project. This can be done by replacing `pubspec.yaml` with this:

```yaml
name: flutter_spaceships
description: A new Flutter project.

publish_to: 'none'
version: 1.0.0+1

environment:
  sdk: '>=2.7.0 <3.0.0'

dependencies:
  flutter:
    sdk: flutter
  ferry: ^0.9.4
  ferry_flutter: ^0.4.0
  gql_http_link: ^0.3.2
  flutter_dotenv: ^3.1.0
  cupertino_icons: ^1.0.0

# This one is important to prevent build_runner errors
dependency_overrides:
  analyzer: ^0.40.4

dev_dependencies:
  flutter_test:
    sdk: flutter
  ferry_generator: ^0.3.3
  build_runner: ^1.10.1

flutter:
  uses-material-design: true
  assets:
    - .env
```

Your IDE may automatically download these packages. If not, you can also run:

```bash
flutter pub get
```

Most GraphQL APIs will require some kind of authorization token. We are not going to implement user authentication in this app, so for now we will use a single token and manage it with a `.env` file.

_Note: Production apps should almost always implement user authentication. We are only skipping it for this tutorial because it takes extra work to set up that isn’t directly related to getting started with GraphQL._

First, you need to get a token from Fauna. From the Fauna dashboard, navigate to **Security** and then select **New Key**.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d8ukxr3d94eznlr1bz7s.png)

From here, we’ll select the **Server** role. You don’t need to specify a key name, but I called mine “development”.

_Note: We are using the Server key for this tutorial but you would not typically want to include a Server key in your production app. You either want to implement proper user authentication or implement another role with limited access. I put together [A Brief Guide on Public Roles in Fauna](/fauna-public-role) in case you need it._

After you save your key, copy it and paste it into a new file named `.env` in the root of your project like so:

```bash
# .env
FAUNA_KEY=your-key-goes-here
```

_Note: If you commit this project to source control, be sure to add the .env to your .gitignore._

Next, open the `main.dart` file.

We won’t preserve any of the boilerplate in this file that was created when we first ran `flutter pub create`, so go ahead and delete all of that.

At the top of the file, let’s add a few import statements.

```dart
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart' as DotEnv;
import 'package:ferry/ferry.dart';
import 'package:gql_http_link/gql_http_link.dart';
```

The [flutter_dotenv](https://pub.dev/packages/flutter_dotenv) package will help us read the `FAUNA_KEY` environment variable we stored in our `.env` file.

Then we are importing Ferry, our GraphQL client, and then [gql_http_link](https://pub.dev/packages/gql_http_link), which will allow us to access our GraphQL API over HTTP.

Next, we will instantiate the Ferry GraphQL client in the `main` function.

```dart
void main() async {
  await DotEnv.load();
  final client = Client(
    link: HttpLink("https://graphql.fauna.com/graphql", defaultHeaders: {
      'Authorization': 'Bearer ${DotEnv.env["FAUNA_KEY"]}',
    }),
  );
  runApp(App(client));
}
```

You can see that we only need to provide two things to interact with the GraphQL API: the endpoint and the `Authorization` header that includes the `FAUNA_KEY` we created earlier. Without the header, Fauna will not permit our app to access the API.

In a more complicated app, you would most likely use something like [get_it](https://pub.dev/packages/get_it) or [provider](https://pub.dev/packages/provider) to make it easier to access the client. To keep this example simple and focused on working with GraphQL, we will simply pass it down to the `App` widget, which we will create next.

```dart
class App extends StatelessWidget {
  final Client _client;
  App(this._client);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Spaceships’,
      theme: ThemeData(
        primarySwatch: Colors.indigo,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: HomeScreen(_client), // We will implement this later
    );
  }
}
```

This `App` is very similar to the default one created when we started the project. The main difference is that we added the `_client` as a constructor argument so that we can pass it to the `HomeScreen`, which is where we will execute a GraphQL query and display some data.

But before we can build the `HomeScreen` widget, we need to write our GraphQL query and make it executable in our Flutter app.

## Write a GraphQL query in a Flutter app

The home screen in our Flutter app will display a list of spaceships, answering the question:

**What spaceships are in my fleet?**

Thankfully, we already proved that we can answer this question when we were writing queries in Fauna’s GraphQL Playground.

Go ahead and copy this content into a file called `spaceships.graphql` (it should live in `/lib` as well).

```graphql
# lib/spaceships.graphql
query GetSpaceships {
  spaceships {
    data {
      _id
      name
    }
  }
}
```

If you compare this query to the one we previously wrote, you will note one important difference. In this one, we added an [operation name](https://graphql.org/learn/queries/#operation-name) to our query: `GetSpaceships`. As we will see in a moment, the operation name is required for Ferry’s type generation.

## Generate Dart classes from a GraphQL query

One of the benefits of Ferry, and why I prefer it over some other GraphQL Flutter clients, is its support for code generation and fully typed data access. This allows us to confidently read and write data with the added convenience of IDE autocompletion.

The first step toward code generation with Ferry is adding our GraphQL schema to the project. To get this, we need to download a schema file from Fauna. Ferry will use this schema file to generate Dart classes from our data schema.

In the GraphQL Playground we were using before, just under the **Docs** tab is a **Schema** tab. Select **Schema**, then **Download** and then **SDL** to download the file.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wzckb7i0k1zujfvse6ad.png)

Save the file in your `/lib` directory like so:

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wxfegx9hucsq4ajd8p3e.png)

If you open `schema.graphql`, what you see is a reflection of what we see when we open the **Docs** tab in Fauna’s GraphQL playground. It’s the same information, just in a different format that can be read by Ferry to generate Dart classes.

Next, we need to create a [build.yaml](https://pub.dev/packages/build_config) file at the root of our project and populate it with this content:

```yaml
targets:
  $default:
    builders:
      gql_build|schema_builder:
        enabled: true
      gql_build|ast_builder:
        enabled: true
      gql_build|data_builder:
        enabled: true
        options:
          schema: flutter_spaceships|lib/schema.graphql
      gql_build|var_builder:
        enabled: true
        options:
          schema: flutter_spaceships|lib/schema.graphql
      gql_build|serializer_builder:
        enabled: true
        options:
          schema: flutter_spaceships|lib/schema.graphql

      ferry_generator|req_builder:
        enabled: true
        options:
          schema: flutter_spaceships|lib/schema.graphql
```

_Note: If you used a different project name other than flutter_spaceships, be sure to replace the schema declarations with whatever you chose._

We just added a fair amount of config, but thankfully you don’t need to understand all of it to make use of it. The important thing is that we now have the correct pieces in place to generate our types by running a single command:

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

Here we are making use of the [build_runner](https://pub.dev/packages/build_runner) package that we included in our dependencies when we first created the Flutter project.

After that runs successfully, you should see several new files in your project that end with `.gql.dart` or `.gql.g.dart`. Notably, several of these are prefixed with `spaceships` and contain classes specifically generated from the `spaceships.graphql` query we previously added.

Now let’s see how we can make use of these classes and fetch some data in our Flutter app.

## Populate a ListView with a GraphQL query

Since our home screen will display a list of spaceships, the [ListView](https://api.flutter.dev/flutter/widgets/ListView-class.html) widget is a good choice. But before we can populate our list, we need to import our newly generated classes.

```dart
# lib/main.dart
import 'package:ferry_flutter/ferry_flutter.dart';
import 'spaceships.req.gql.dart';
import 'spaceships.var.gql.dart';
import 'spaceships.data.gql.dart';
```

[Ferry Flutter](https://pub.dev/packages/ferry_flutter) provides an [Operation](https://pub.dev/documentation/ferry_flutter/latest/ferry_flutter/Operation-class.html) widget that makes it easier to work with GraphQL in Flutter apps. To use this widget, we will implement the `HomeScreen` widget like so:

```dart
class HomeScreen extends StatelessWidget {
  final Client _client;

  HomeScreen(this._client);

  @override
  Widget build(BuildContext context) {
    return Operation<GGetSpaceshipsData, GGetSpaceshipsVars>(
      client: _client,
      operationRequest: GGetSpaceshipsReq(),
      builder: (context, response, error) {
        return Scaffold(
          appBar: AppBar(
            title: Text("Flutter Space Fleet"),
          ),
          body: response.loading
              ? Text('Loading...')
              : SpaceshipList(response.data.spaceships),
        );
      },
    );
  }
}
```

The `Operation` widget is making use of a few classes prefixed with `GGetSpaceship`. Earlier we mentioned the importance of the operation name in the GraphQL query we defined in `spaceships.graphql` and here we see how Ferry used it to generate unique classes just for that query.

By passing `GGetSpaceshipsReq` as the `operationRequest`, we are telling the `Operation` widget to execute our GraphQL query and then provide the results of the query to the widgets built inside the `builder` method. On the first build, the data will not be present yet as Ferry waits for a network response, so we display basic loading text.

After the network response is received, we can then render the data in the `SpaceshipList`, which is defined below.

```dart
class SpaceshipList extends StatelessWidget {
  final GGetSpaceshipsData_spaceships _spaceships;
  SpaceshipList(this._spaceships);

  @override
  Widget build(BuildContext context) {
    return ListView.builder(
      itemCount: _spaceships.data.length,
      itemBuilder: (context, i) {
        final spaceship = _spaceships.data[i];
        return ListTile(
          title: Text(spaceship.name),
          trailing: Text('🚀'),
        );
      },
    );
  }
}
```

`GGetSpaceshipsData_spaceships` may look like a funny name for a type, but it’s structure makes sense when you look again at the query that generated these types:

```graphql
query GetSpaceships {
  spaceships {
    data {
      _id
      name
    }
  }
}
```

Since GraphQL queries can be nested, Ferry will generate new classes to capture these structures.

Since the `data` property under `spaceships` is a GraphQL array, it becomes a list in our Flutter app, meaning we can iterate, map filter, etc. just like any other list.

In our case, we are making use of [ListView.builder](https://api.flutter.dev/flutter/widgets/ListView/ListView.builder.html), which can work with any list including our GraphQL generated list. If you inspect the class of the `spaceship` variable that we define in the `itemBuilder`, you can see another class generated by Ferry: `GGetSpaceshipsData_spaceships_data`.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l2m2e60wwzf9ro9asv6f.png)

Do you see the pattern yet?

```graphql
query GetSpaceships {
  spaceships {
    data {
      _id
      name
    }
  }
}
```

When we get to the leaf nodes of the query (the innermost nodes: `_id` and `name`) we find data in a format that we can display. The `name` field is a `String` and can be displayed for each spaceship in the list.

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sizsss5ekavovadfyntf.png)

## Summary

In the first part of this series, we created a GraphQL API in Fauna and were able to integrate it in a Flutter app.

With most of the setup out of the way, adding new widgets that also require data from the GraphQL API will only need a few steps:
- Write a query in a `.graphql` file.
- Build classes with `build_runner`.
- Build a widget that uses the `Operation` widget and displays the data.

Find the full source code on GitHub: [https://github.com/seanconnollydev/flutter_spaceships](https://github.com/seanconnollydev/flutter_spaceships)

In the next post of this series, we will explore how to add and edit spaceships with GraphQL mutations and see how the normalized cache keeps our application state up to date.