Generating Models with Artemis for GraphQL Mutations in Flutter

We are going to describe how to generate models with Artemis for mutations. In the previous blog post, we already showed you to insert a new object at a GraphQL endpoint with a mutation. One of the things that I disliked, coming from more typed based languages (Kotlin, Java) was the way we had to convert the Dart DateTime to a different format before we could insert it. In this blog post, we are going to describe how to generate models for the input of the race and how to incorporate it in the form!

Generate the models

In another blog post, we showed how to generate models for the mutations. We will show you how to do the same for mutations. Type safety is easier to achieve and mutations can be validated more easily because they can be saved as GraphQL file. Before we start with adding the mutations we have to add some dependencies and set up the build file.

dependencies:
  flutter:
    sdk: flutter
  graphql_flutter: ^3.0.0
  json_serializable: ^3.0.0
  meta: '>=1.0.0 <2.0.0'
  gql: '>=0.7.3 <1.0.0'

dev_dependencies:
  artemis: ^5.1.0
  build_runner: ^1.5.0
  flutter_test:
    sdk: flutter

If you are using Android Studio or IntelliJ you can add a .graphqlconfig file for your GraphQL endpoint. Otherwise, you can use a plugin to download the schema.graphql. The config file should point to the URL of the endpoint and have a reference to where the schema.graphql should be saved.

{
  "name": "Formula One GraphQL",
  "schemaPath": "schema.graphql",
  "extensions": {
    "endpoints": {
      "Default GraphQL Endpoint": {
        "url": "https://brief-quagga-80.hasura.app/v1/graphql",
        "headers": {
          "user-agent": "JS GraphQL"
        },
        "introspect": true
      }
    }
  }
}

This will download the schema.graphql which we will need to generate the models for our query. If this does not run, make sure you have the JS GraphQL IntelliJ Plugin installed. Now we have the schema, we are going to add the mutation to the project.

mutation InsertRace($date: date, $name: String, $season_id: uuid, $round: Int) {
    insert_Race(objects: [{date: $date, name: $name, season_id: $season_id, round: $round}]) {
        returning {
            name
        }
    }
}

We can now add the build.yaml to configure Artemis. Here we tell Artemis where to generate the code, where to find the schema.graphql, and where to find our queries. We did also add two custom parsers, which are not known by the plugin. The custom parsers will convert the GraphQL types for date and UUID to the corresponding Dart objects.

targets:
  $default:
    sources:
      - lib/**
      - graphql/**
      - schema.graphql
    builders:
      artemis:
        options:
          schema_mapping:
            - custom_parser_import: 'coercers.dart'
              graphql_type: date
              dart_type: DateTime
            - custom_parser_import: 'coercers.dart'
              graphql_type: uuid
              dart_type: String
            - schema: schema.graphql
              queries_glob: graphql/*.graphql
              output: lib/graphql_api.dart

So how do the coercers look like? We will simply map from the GraphQL value represented as String to the value we want. For the date, this means that we convert from (GraphQL date) String to (Dart) DateTime and back. For the UUID we do the same, here we can simply map to String, as that is the type we would like to use in Dart and does not differ from the format for the GraphQL type UUID.

final dateFormatter = DateFormat('yyyy-MM-dd');
final timeFormatter = DateFormat('HH:mm:ss');

DateTime fromGraphQLDateToDartDateTime(String date) => DateTime.parse(date);
String fromDartDateTimeToGraphQLDate(DateTime date) =>
    dateFormatter.format(date);
String fromGraphQLUuidToDartString(String uuid) => uuid;
String fromDartStringToGraphQLUuid(String uuid) => uuid;

We can now start with the generation of the models with the following command:

pub run build_runner build

This should generate the models! We can now change the MutationTrigger Widget to use those models instead of the mutation string.

Using the generated models in our Widget

We have now generated the models, let’s use them. This is pretty simple. We can create an instant of the InsertRaceArguments. Here we supply the variables that we saved in the form. Now we can use the client again to mutate the mutation. We can also replace the String with the mutation by the InsertRaceMutation().document.

InsertRaceArguments arguments = InsertRaceArguments(
   date: raceDate,
   round: round,
   name: name,
   season_id: 'f8e7d546-7556-4ccf-9615-7186c7d6a113');
await graphQLConfiguration.client.mutate(MutationOptions(
   documentNode: InsertRaceMutation().document,
   variables: arguments.toJson()));

This solves a lot of issues with typing and makes it easier to edit the mutations in a GraphQL file, which reduces the risks of errors when defining your queries or mutations. We no longer have to think about how the date should be formatted for the GraphQL endpoint. We can simply provide our date as Dart DateTime. If you want to check out the code, you can find it here! Thank you for reading and if you still have any questions, feel free to ask them.

Leave a Reply