AWS Amplify For Flutter Part 5: GraphQL API

AWS Amplify For Flutter Part 5: GraphQL API

In this fifth exploration of the series, we shall see how to integrate GraphQL in your project for further data exploration

Β·

8 min read

So far in the AWS Amplify For Futter series, we have seen Introduction and Setup of Amplify, Amplify DataStore, Amplify Authentication, and Amplify Storage. We will continue to explore Amplify for Flutter in this article with Amplify GraphQL.

Unlike Firestore which only has one, Amplify has a couple of ways to access data from the database. We can either use Amplify DataStore or we can use managed APIs provided by Amplify to access the data. There are some upsides and downsides of using both. When it comes to Amplify APIs, you again have two choices of using either REST APIs or GraphQL APIs. Let us see the implementation of GraphQL APIs.

Amplify API

Amplify API is an interface that helps in persisting and fetching data models. The API category of Amplify relies on AWS AppSync and provides all the functionalities of AppSync.

Setting up GraphQL APIs in your project is very easy if you are using Amplify CLI. Amplify CLI can help in provision managing GraphQL services by providing CRUD as well as real-time functionalities.

These are the topics that this article will explore:

  • How do GraphQL APIs function?
  • Setting up Amplify GraphQL APIs
  • Authorization models for APIs
  • CRUD operations
  • Real-time GraphQL subscriptions

How do GraphQL APIs function?

As we know, Amplify GraphQL APIs use AWS AppSync for their various functionalities. AppSync is used to make a highly scalable application that can support real-time data fetching and updating. AppSync can be integrated with multiple data sources including relational databases, NoSQL databases, HTTP APIs and even your own data source right from AWS Lambda. The AppSync mobile SDKs provide the additional functionality of persisting data locally when the device is offline. It also has custom conflict resolution and data sync functionalities when the device is online.

Amplify GraphQL API uses AWS Signature version 4 singer for authentication. Amplify will automatically sign all the API calls for you while also configuring the authorization mode. Some popularly available authorization modes are AWS IAM, Amazon Cognito Pools, 3rd party OpenID Connect and API Keys.

When not to use Amplify GraphQL APIs

GraphQL is a lightweight option for data fetching and updating. If you are looking for something that has offline capabilities or caching, then GraphQL is probably not the best option for you. Amplify DataStore is a better option for offline data and caching. You can still use GraphQL and persist the data manually but I think it's best if we leave the heavy lifting of persisting and caching to Amplify and focus on our app. So if you are looking for any of these functionalities I suggest you read my article on Amplify DataStore.

Setting up Amplify GraphQL APIs

  • Make sure you have Amplify CLI installed properly after you set up your project. Execute the following command from the root directory of your project:
amplify add api

When asked for configuration follow the below steps,

? Please select from one of the below-mentioned services: 
    `GraphQL`
? Provide API name: 
    `apiName`
? Choose the default authorization type for the API:
    `API key`
? Enter a description for the API key:
    `description`
? After how many days from now the API key should expire (1-365): 
    `7`
? Do you want to configure advanced settings for the GraphQL API 
    `No, I am done.`
? Do you have an annotated GraphQL schema? 
    `No`
? Choose a schema template:
    `Single object with fields (e.g., β€œTodo” with ID, name, description)`
? Do you want to edit the schema now? 
    `No`
  • After completion of setup, you will see a schema file at amplify/backend/api/{api_name}/schema.graphql. This is where you can define the scheme of your project. You can also update your schema via Amplify console which will give you a GUI for creating your schema.
  • You have to push your changes via the following command after creating your schema:
amplify push
  • This will update the relevant database with respect to the model you have pushed. If you have used the GUI version to create your schema and published it there, then you have to run the following command to get those schemas updated locally:
amplify pull
  • Once everything is completed, you will see that the amplifyconfiguration.dart file has credentials and references related to the Backend along with AppSync services allocated to your project.

Note: You should never commit this file to your version tool. Always add it to the .gitignore file.

Adding Amplify Libraries

  • Open your pubspec.yaml file and add the following dependencies:
 amplify_flutter: '<1.0.0'
  amplify_api: '<1.0.0'
  • Add Amplify API category to Amplify.addPlugin() method. Following is an example of how an initialization call should look like:
import 'package:amplify_flutter/amplify.dart';
import 'package:amplify_api/amplify_api.dart';

import 'amplifyconfiguration.dart';

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
    @override
    void initState() {
        super.initState();
        _configureAmplify();
    }

    void _configureAmplify() async {
        // Add the following line to add API plugin to your app
        Amplify.addPlugin(AmplifyAPI());

        try {
            await Amplify.configure(amplifyconfig);
        } on AmplifyAlreadyConfiguredException {
            print("Tried to reconfigure Amplify; this can occur when your app restarts on Android.");
        }
    }
}

Authorization models for APIs

Amplify currently supports these 4 authorization methods:

  • API keys
  • Amazon IAM credentials
  • Amazon Cognito User Pools
  • 3rd Party OIDC providers

The best thing about GraphQL authorization is that you don't have to do anything; it just works. All you have to do is just tell Amplify what you want to use when you are setting up and everything is taken care automatically. Let's talk about each mode.

API Key

API keys are lightweight and easiest to use when you are prototyping. On the contrary, it's easy to abuse the API key since anyone who has your API key can make a request to your server. It's recommended to use more advanced authorization techniques like Cognito pools or AWS IAM. The API key also comes with an expiry time which you can configure while setting up or calling amplify update api but you will have to update the key after its expiry.

Amazon Cognito

Amazon Cognito user pools are used the most with GraphQL APIs. With this mode of authorization, Amplify will automatically add the token of the logged-in users with each request. If you are using AWS services other than AppSync, you might have to use a combination of Amazon Cognito and Amazon IAM. This combination will allow you to use both user pools for AppSync requests and AWS IAM users for using AWS resources.

AWS IAM (Identity and Access Management)

You can activate AWS IAM by adding Amazon Cognito and the following code under awsAPIPlugin in the amplifyconfiguration.dart file:

{
    ...
    "awsAPIPlugin": {
        "<YOUR-GRAPHQLENDPOINT-NAME": {
            "endpointType": "GraphQL",
            "endpoint": "[GRAPHQL-ENDPOINT]",
            "region": "[REGION]",
            "authorizationType": "AWS_IAM",
        }
    }
}

OIDC

OIDC is not yet supported on Flutter. You can follow this Github Issue for updates.

Fetching data

First, you have to create your query string. You can test your queries from the AWS AppSync console directly. You don't have to attach any authorization token to it; Amplify will add the credentials internally. Once you have your query, call the Amplify.API.query() function as shown below:

try {
    String graphQLDocument = '''query ListTodos {
      listTodos {
        items {
          id
          name
          description
        }
        nextToken
      }
    }''';

    var operation = Amplify.API.query(
        request: GraphQLRequest<String>(document: graphQLDocument)
    );

    var response = await operation.response;
    var data = response.data;

    print('Query result: ' + data);
} on ApiException catch (e) {
    print('Query failed: $e');
}

Create and update queries/mutations

  • You can simply assign your mutation to a string and call it using Amplify.API.mutate as shown below:
try {
    String graphQLDocument =
        '''mutation CreateTodo(\$name: String!, \$description: String) {
              createTodo(input: {name: \$name, description: \$description}) {
                id
                name
                description
              }
        }''';

    var operation = Amplify.API.mutate(
        request: GraphQLRequest<String>(document: graphQLDocument, variables: {
      'name': 'my first todo',
      'description': 'todo description',
    }));

    var response = await operation.response;
    var data = response.data;

    print('Mutation result: ' + data);
} on ApiException catch (e) {
  print('Mutation failed: $e');
}
  • The response from the above request should look like this :
{
  "createTodo": {
    "name": "my first todo",
    "id": "571f87e656a0-34db13-4d87c4b1-c4b1-3f9387c4",
    "description": "todo description"
  }
}
  • You can always decode the data onto a Map and use it with the following code snippet:
Map result = json.decode(response.data);
Map todoMap = result['createTodo'];
  • The update and delete mutation work exactly the same way. You can call your mutation with Amplify.API.mutate and pass through the variable.

Real-time GraphQL subscriptions

We can listen to different GraphQL methods in Amplify. Below is an example for a OnCreateTodo mutation:

try {
    String graphQLDocument = '''subscription OnCreateTodo {
        onCreateTodo {
          id
          name
          description
        }
      }''';

    var operation = Amplify.API.subscribe(
        request: GraphQLRequest<String>(document: graphQLDocument),
        onData: (event) {
          print('Subscription event data received: ${event.data}');
        },
        onEstablished: () {
          print('Subscription established');
        },
        onError: (e) {
          print('Subscription failed with error: $e');
        },
        onDone: () {
          print('Subscription has been closed successfully');
        });
} on ApiException catch (e) {
    print('Failed to establish subscription: $e');
}

Similarly, you can listen to the mutation, update it and delete it as well. If you wish to unsubscribe from updates, call operation.cancel(); and simply dispose the widget.

Troubleshooting

AppSync has the provision of conflict detection but Amplify APIs do not give an inbuilt solution for it. You have to use Amplify DataStore to get the benefits of conflict detection and resolution. You will receive an error on every update and delete request because of the missing _version and _lastChangedAt variable in the GraphQL APIs. You have to explicitly add these parameters or find another option to completely disable conflict detection.

Conclusion

That's all for AWS Amplify GraphQL APIs! Amplify GraphQL is a promising option for those who are are familiar with GraphQL. In this article, we have seen how easy it is to use GraphQL APIs in your project; you can do real-time data fetching, send CRUD requests and retain the security of your APIs with AWS Authentication. AppSync handles all the requests and data for the Backend and you can always change the AppSync configuration from your AppSync Console.

Thank you so much for reading 😁 and sticking through till the end of the series. If you like this article, then please leave a πŸ‘ and come say hi on Twitter.

Β