Front-End Web & Mobile

Explore AWS AppSync APIs with GraphiQL from your local machine

This article was written by Eduardo Rabelo, Solutions Architect, AWS

 

 

AWS AppSync is a managed GraphQL service that simplifies application development by letting you create a flexible API to securely access, manipulate, and combine data from one or more data sources with less network calls. With AWS AppSync you can build scalable applications, including those requiring real-time updates, on a range of data sources such as NoSQL data stores, relational databases, HTTP APIs, and your custom data sources with AWS Lambda.

GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. One of these tools is GraphiQL, which provides a graphical in-browser GraphQL development environment enabling an interactive playground to compose, test, and see the live results of your queries. It also allows to easily access and navigate the automatically generated GraphQL API documentation leveraging schema introspection.

While GraphiQL is integrated in the Queries section of a GraphQL API in the AWS AppSync Console, it’s possible in a development project that just few selected team members have AWS credentials or access to the AWS console. Some developers just need to inspect the API contract in addition to prototype and test GraphQL queries, nothing else. In these scenarios, it’s possible to leverage different AWS AppSync authorization modes to connect a local GraphiQL instance to your remote AppSync API.

 

 

GraphQL endpoint

You can use an existing AppSync API or follow this quick start tutorial to create a new GraphQL API in minutes. In order to connect a local GraphiQL instance to AppSync we need to know the GraphQL API endpoint. The endpoint can be retrieved from the AWS AppSync Console: click on Settings on the side bar and, under the API Details section, you find the API URL details.

 

 

Credential methods

With AWS AppSync, you create GraphQL APIs that your applications interact with over the internet. While the API endpoints are publicly reachable, they never allow unauthorized access. A method of authorization — a token in the request header or signing the request itself with AWS credentials — is always required to access your AppSync API.

AWS AppSync APIs supports a variety of methods to authenticate and authorize a request. In this article, we showcase how to access an AppSync API using three different authorization modes.

  • API key
  • Amazon Cognito User Pools
  • AWS IAM

With API Keys we can inspect the schema of the API and interact with fields that don’t require a specific identity such as a user ID. With IAM we sign the request with AWS Signature Version 4. Finally, with Cognito we use a valid access token generated with the AWS CLI so we are able to perform all operations an authenticated user can.

 

Using API keys

We use the GraphiQL CDN example from GitHub to get started. Copy the contents of the index.html file to your local machine and find the function graphQLFetcher. Replace the function code updating the API endpoint and API key retrieved from the AppSync console accordingly:

 

function graphQLFetcher(graphQLParams) {
  const APPSYNC_API_URL = "TYPE_YOUR_APPSYNC_URL";
  const credentialsAppSync = {
    "x-api-key": "TYPE_YOUR_API_KEY"
  }
  return fetch(
    APPSYNC_API_URL,
    {
      method: "post",
      headers: {
        Accept: "application/json",
        "Content-Type": "application/json",
        ...credentialsAppSync
      },
      body: JSON.stringify(graphQLParams),
      credentials: "omit",
    }
  ).then(function (response) {
    return response.json().catch(function () {
      return response.text();
    });
  });
}

 

By opening the modified index.html file in a browser you can confirm a successful connection and see the remote schema documentation by clicking Docs on the top right:

 

 

 

 

User authorization with Cognito

We use the AW CLI to initiate the authentication flow for an existing user in a Cognito User Pool with the following command:

aws cognito-idp initiate-auth \
  --auth-flow USER_PASSWORD_AUTH \
  --client-id __TYPE_YOUR_USER_POOL_CLIENT_ID__ \
  --auth-parameters \
    USERNAME=__TYPE_YOUR_USERNAME__,PASSWORD=__TYPE_YOUR_USER_PASSWORD__

The command returns a couple of tokens:

{
    "ChallengeParameters": {},
    "AuthenticationResult": {
        "AccessToken": "eyJraWQiOiI1dVUwMld...",
        "ExpiresIn": 3600,
        "TokenType": "Bearer",
        "RefreshToken": "eyJjdHkiOiJKV1...",
        "IdToken": "eyJraWQiOiJVU..."
    }
}

Update the index.html with the following code using the access token retrieved in the previous step:

function graphQLFetcher(graphQLParams) {
  const APPSYNC_API_URL = "TYPE_YOUR_APPSYNC_URL";
  const credentialsAppSync = {
    Authorization: "eyJraWQiOiI1dVUwMld...",
  };
  return fetch(APPSYNC_API_URL, {
    method: "post",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
      ...credentialsAppSync,
    },
    body: JSON.stringify(graphQLParams),
    credentials: "omit",
  }).then(function (response) {
    return response.json().catch(function () {
      return response.text();
    });
  });
}

Now we are able to perform any operation (e.g. Query or Mutation) that requires a specific user identity. For example, a query to read the current profile:

query readMyProfile {
  readMyProfile {
    id
    name
    screenName
    bio
  }
}

 

Or a mutation to update the current profile:

mutation updateMyProfile {
  updateMyProfile(input: { name: "Local" bio: "GraphiQL" }) {
    id
    name
    screenName
    bio
  }
}

 

 

 

Signed requests with IAM

In order to use IAM on the client-side you need to sign a request with appropriate AWS credentials. You can find out how to create an IAM user, define policies and retrieve credentials for programmatic access in the IAM documentation. Make sure to grant least privilege access when defining IAM policies. Generally it’s recommended to use Cognito Identity Pools to assume an IAM role in a web application so AWS keys are not hard coded in the client, you can find more information on how to do so in the documentation.

Using IAM Policies attached to an IAM user we can restrict access to certain GraphQL operations, such as Query, Mutation, and/or Subscription fields:

 

{"Version": "2012-10-17",
   "Statement": [
      {"Effect": "Allow",
         "Action": [
            "appsync:GraphQL"
         ],
         "Resource": [
            "arn:aws:appsync:<AWS_REGION>:<AWS_ACCOUNT_ID>:apis/<API_NAME>/types/Query/fields/<Field-1>",
            "arn:aws:appsync:<AWS_REGION>:<AWS_ACCOUNT_ID>:apis/<API_NAME>/types/Mutation/fields/<Field-1>",
            "arn:aws:appsync:<AWS_REGION>:<AWS_ACCOUNT_ID>:apis/<API_NAME>/types/Subscription/fields/<Field-1>"
         ]
     }
   ]
}

 

Next we need to import the AWS SDK. Go to the SDK Builder and download the latest version of the AWS SDK after selecting the default services. Copy the SDK to the same folder as the index.html file and append the following code before the </head> tag:

 

<head>
  ...
  <script 
    src="PATH TO AWS SDK"
    type="application/javascript"></script>
</head>

 

Now update the graphQLFetcher code accordingly with the required details. While AWS credentials are directly used in this sample, the purpose of this code is to be executed in a local machine for testing and prototyping only. Do not use this approach in production:

 

const AWS_REGION = "TYPE_YOUR_AWS_REGION";
const AWS_ACCESS_KEY_ID = "TYPE_YOUR_IAM_ACCESS_KEY_ID";
const AWS_SECRET_ACCESS_KEY = "TYPE_YOUR_IAM_SECRET_ACCESS_KEY";

AWS.config.update({
  region: AWS_REGION,
  credentials: new AWS.Credentials(
    AWS_ACCESS_KEY_ID,
    AWS_SECRET_ACCESS_KEY
  ),
});

function graphQLFetcher(graphQLParams) {
  const APPSYNC_API_URL = "TYPE_YOUR_APPSYNC_URL";

  const uri = new URL(APPSYNC_API_URL);
  const httpRequest = new AWS.HttpRequest(uri.href, AWS_REGION);
  httpRequest.headers.host = uri.host;
  httpRequest.headers["Content-Type"] = "application/json";
  httpRequest.method = "POST";
  httpRequest.body = JSON.stringify(graphQLParams);

  return new Promise((res, rej) => {
    AWS.config.credentials.get((err) => {
      const signer = new AWS.Signers.V4(httpRequest, "appsync", true);
      signer.addAuthorization(
        AWS.config.credentials,
        AWS.util.date.getDate()
      );

      const options = {
        method: httpRequest.method,
        body: httpRequest.body,
        headers: httpRequest.headers,
      };

      return fetch(uri.href, options)
        .then((req) => req.json())
        .then((json) => res(json))
        .catch(rej);
    });
  });
}

 

As before, you can test the connection is successful by navigating the GraphQL Schema on the Docs sidebar or executing allowed operations using IAM authorization.

 

 

Conclusion

With the appropriate authorization methods defined in your AppSync GraphQL API your team can inspect a GraphQL schema and prototype queries from their local machine leveraging a local GraphiQL instance from their browsers. Going one step further into local development, it’s possible to leverage the Amplify CLI testing capabilities and have AppSync running in your laptop with local mocking which also includes a customized GraphiQL instance for prototyping queries.

Developers can easily mix and match authorization modes in the same API with multi-auth support. You can create different API keys, users in Cognito, or IAM users or roles with appropriate policies for different team members to test different access patterns and use cases in your API without the need to access the AWS Console.