Building Full Stack Serverless Application With Amplify, Flutter, GraphQL, AWS CDK, and Typescript(PART 4)

Building Full Stack Serverless Application With Amplify, Flutter, GraphQL, AWS CDK, and Typescript(PART 4)

Complete Source Code for the GraphQL CDK API
Complete Source Code for the Flutter App

Here's the Last Article in a 4 part series. We'll continue from where we left off in part 3. Part 1
Part 2
Part 3


I'm Assuming you already have the following installed and configured on your computer

Create Flutter Project

Create a new Flutter project, name it whatever you like.

flutter create notes_app

Add these plugins to your pubspec.yml file


environment:
  sdk: ">=2.12.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter


  # The following adds the Cupertino Icons font to your application.
  # Use with the CupertinoIcons class for iOS style icons.
  cupertino_icons: ^1.0.2

dev_dependencies:
  flutter_test:
    sdk: flutter
  amplify_flutter: ^0.2.0
  amplify_api: ^0.2.0
  amplify_auth_cognito: ^0.2.0
  flutter_staggered_grid_view: ^0.4.0
  provider: ^5.0.0
  timeago: ^3.1.0
  flutter_svg: ^0.22.0

Install the dependencies by running the following command

flutter pub get

Update target iOS platform

From your project root, navigate to the ios/directory and modify the Podfile using a text editor of your choice and update the target iOS platform to 13.0 or higher.

platform :ios, '13.0'

Update target Android SDK version

From your project root, navigate to the android/app/ directory and modify build.gradle using a text editor of your choice and update the target Android SDK version to 21 or higher:

minSdkVersion 21

Next, configure Amplify in the root of your projects directory by using

amplify init

After successfully setting up amplify, you can add an auth category using the command

amplify add auth

Setup authentication with username.

Navigate to your project in appsync, and download the configuration file.

Screen Shot 2021-09-17 at 07.26.35.png Navigate to lib/amplifyconfiguration.dart and add the settings from the config file you downloaded above.
Here's how mine looks like

const amplifyconfig = ''' {
    "UserAgent": "aws-amplify-cli/2.0",
    "Version": "1.0",
    "api": {
        "plugins": {
            "awsAPIPlugin": {
               "cdk-notes-appsync-api_API_KEY": {
                    "endpointType": "GraphQL",
                    "endpoint": "ADD YOUR ENDPOINT HERE",
                    "region": "us-east-2",
                    "authorizationType": "API_KEY",
                    "apiKey": "ADD YOUR API KEY HERE"
                },
                 "cdk-notes-appsync-api_AMAZON_COGNITO_USER_POOLS": {
                    "endpointType": "GraphQL",
                    "endpoint": "endpoint": "ADD YOUR ENDPOINT HERE",
                    "region": "us-east-2",
                    "authorizationType": "AMAZON_COGNITO_USER_POOLS"

                }


            }
        }
    },
    "auth": {
        "plugins": {
            "awsCognitoAuthPlugin": {
                "UserAgent": "aws-amplify-cli/0.1.0",
                "Version": "0.1.0",
                "IdentityManager": {
                    "Default": {}
                },
                "AppSync": {
                    "Default": {
                        "ApiUrl": "endpoint": "ADD YOUR ENDPOINT HERE",
                        "Region": "us-east-2",
                        "AuthMode": "API_KEY",
                        "ApiKey": "da2-2gx4ibbvdrgnvn2nf7aid5nggu",
                        "ClientDatabasePrefix": "cdk-notes-appsync-api_API_KEY"
                    }
                },
                "CredentialsProvider": {
                    "CognitoIdentity": {
                        "Default": {
                            "PoolId": "us-east-2:ad0dbf3f-b8a1-4934-9a2f-c5c0e8d25a29",
                            "Region": "us-east-2"
                        }
                    }
                },
                "CognitoUserPool": {
                    "Default": {
                        "PoolId": "us-east-2_GLX8eEo1l",
                        "AppClientId": "665uc7vb8ac7cincds1m5rgdkj",
                        "Region": "us-east-2"
                    }
                },
                "Auth": {
                    "Default": {
                        "authenticationFlowType": "USER_SRP_AUTH"
                    }
                }
            }
        }
    }
}''';

"cdk-notes-appsync-api_API_KEY". grants public access, while "cdk-notes-appsync-api_AMAZON_COGNITO_USER_POOLS" grants private access.

Finally, go to your cdk project in AWS Cognito console and make sure the AppClientId and PoolId in lib/amplifyconfiguration.dart are same as those in Cognito

Screen Shot 2021-09-17 at 07.45.19.png

Configure Amplify In Flutter.

In the main.dart file , let's configure the 2 amplify plugins we would be using

 void _configureAmplify() async {

    // Add Pinpoint and Cognito Plugins, or any other plugins you want to use
    //AmplifyAnalyticsPinpoint analyticsPlugin = AmplifyAnalyticsPinpoint();
    AmplifyAuthCognito authPlugin = AmplifyAuthCognito();

    Amplify.addPlugins([authPlugin,AmplifyAPI()]);

    // Once Plugins are added, configure Amplify
    // Note: Amplify can only be configured once.
    try {
      await Amplify.configure(amplifyconfig);


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

What happens above is, we add the auth and api plugins to Amplify and configure them alongside the config file we edited.

  Amplify.addPlugins([authPlugin,AmplifyAPI()]);
  await Amplify.configure(amplifyconfig);

Then we can call this function in the initState.

 @override
  void initState() {
    // TODO: implement initState
    super.initState();
  _configureAmplify();
  }

So every time your app runs, amplify is configured first before any requests are being made.
In the next section, we'll look at code snippets of each function our application is going to have.
Let's get started, shall we

Registration And Login

In order for a user to have authenticated access to the private endpoints, they have to be able to create and verify their account.
In flutter, we'll create a form to retrieve their username, email and password, then using Amplify, we'll send those to Cognito for validation and registration.
Upon successful registration, a verification code(OTP)(One Time Password) would be sent to the user's email, which they'll need to enter it into the app to complete account verification. registration

 Map<String, String> userAttributes = {
        'email': emailController.text.trim(),
        'phone_number': '+15559101234',
        // additional attributes as needed
      };
      SignUpResult res = await Amplify.Auth.signUp(
          username: usernameController.text.trim(),
          password: passwordController.text.trim(),
          options: CognitoSignUpOptions(
              userAttributes: userAttributes
          )
      );

verify account with OTP code

  SignUpResult res = await Amplify.Auth.confirmSignUp(
          username: username,
          confirmationCode: codeController.text.trim());

Login

 SignInResult signInRes = await Amplify.Auth.signIn(
            username: username,
            password: password,
          );

The complete code is available here, in the reg_login_repo.dart file.

List All Notes

The home page of our application would have a staggered gridview of all Notes in our database. Here's the code to grab all notes using graphQL and Amplify


    String graphQLDocument =
    '''query listNotes {
  listNotes {
    color
    description
    id
    title
    createdOn
  }
}''';

    var operation = Amplify.API.query(


        request: GraphQLRequest<String>(document: graphQLDocument,apiName: "cdk-notes-appsync-api_API_KEY"));



    var response = await operation.response;

    final responseJson = json.decode(response.data);
 print("here"+ responseJson.toString());

Take note of the apiName: "cdk-notes-appsync-api_API_KEY which makes the endpoint publicly accessible.

Create Note

This screen should only be accessed by authenticated users.

 String graphQLDocument =
          '''mutation note(\$id: ID!,\$title:String!, \$description: String!,\$color:String!,\$createdOn:AWSTimestamp) {

  createNote(note: 
  {
   id: \$id,
   title:\$title,
   description:\$description,
   color:\$color,
   createdOn:\$createdOn
   }) {
      id
    title
    description
    color

  }
}''';

       var operation = Amplify.API.mutate(
          request: GraphQLRequest<String>(
        document: graphQLDocument,
        apiName: "cdk-notes-appsync-api_AMAZON_COGNITO_USER_POOLS",
        variables: {
          'id': uuid,
          'title': noteTitleController.text,
          'description': noteDescriptionController.text,
          'color': colorSelected,
          'createdOn': dateCreated.microsecondsSinceEpoch,
        },
      ));

      var response = await operation.response;

      var data = response.data;

      print('Mutation result is' + data);
      print('Mutation error: ' + response.errors.toString());

Take note of the Amplify.API.mutate which shows that we are about to perform a mutation. Also pay attention to the apiName: "cdk-notes-appsync-api_AMAZON_COGNITO_USER_POOLS", which makes this API endpoint accessible to authenticated users only.

As an exercise, please implement the remaining endpoint such as

  • getNoteById
  • updateNote
  • deleteNote I'll love to see how you do it.

Conclusion

In this tutorial series, we looked at how to build a full-stack serverless application by leveraging the powerful capabilities of frameworks,languages and tools such

  • GraphQl
  • Typescript
  • AWS CDK
  • Amplify
  • Flutter If you found this piece helpful, please share it with your peers.
    Show a Serverless Hero Some ❤️ by spreading the word.
    Hope you learned something new, I'll be coming through with a ton of stuff. So stay tuned.
    Happy Coding ✌🏿

PEACE