Search code examples
python-3.xboto3aws-appsync

How to send a GraphQL query to AppSync from python?


How do we post a GraphQL request through AWS AppSync using boto?

Ultimately I'm trying to mimic a mobile app accessing our stackless/cloudformation stack on AWS, but with python. Not javascript or amplify.

The primary pain point is authentication; I've tried a dozen different ways already. This the current one, which generates a "401" response with "UnauthorizedException" and "Permission denied", which is actually pretty good considering some of the other messages I've had. I'm now using the 'aws_requests_auth' library to do the signing part. I assume it authenticates me using the stored /.aws/credentials from my local environment, or does it?

I'm a little confused as to where and how cognito identities and pools will come into it. eg: say I wanted to mimic the sign-up sequence?

Anyways the code looks pretty straightforward; I just don't grok the authentication.

from aws_requests_auth.boto_utils import BotoAWSRequestsAuth

APPSYNC_API_KEY = 'inAppsyncSettings'
APPSYNC_API_ENDPOINT_URL = 'https://aaaaaaaaaaaavzbke.appsync-api.ap-southeast-2.amazonaws.com/graphql'

headers = {
    'Content-Type': "application/graphql",
    'x-api-key': APPSYNC_API_KEY,
    'cache-control': "no-cache",
}
query = """{
    GetUserSettingsByEmail(email: "john@washere"){
      items {name, identity_id, invite_code}
    }
}"""


def test_stuff():
    # Use the library to generate auth headers.
    auth = BotoAWSRequestsAuth(
        aws_host='aaaaaaaaaaaavzbke.appsync-api.ap-southeast-2.amazonaws.com',
        aws_region='ap-southeast-2',
        aws_service='appsync')

    # Create an http graphql request.
    response = requests.post(
        APPSYNC_API_ENDPOINT_URL, 
        json={'query': query}, 
        auth=auth, 
        headers=headers)

    print(response)

# this didn't work:
#    response = requests.post(APPSYNC_API_ENDPOINT_URL, data=json.dumps({'query': query}), auth=auth, headers=headers)

Yields

{
  "errors" : [ {
    "errorType" : "UnauthorizedException",
    "message" : "Permission denied"
  } ]
}

Solution

  • graphql-python/gql supports AWS AppSync since version 3.0.0rc0.

    It supports queries, mutation and even subscriptions on the realtime endpoint.

    The documentation is available here

    Here is an example of a mutation using the API Key authentication:

    import asyncio
    import os
    import sys
    from urllib.parse import urlparse
    
    from gql import Client, gql
    from gql.transport.aiohttp import AIOHTTPTransport
    from gql.transport.appsync_auth import AppSyncApiKeyAuthentication
    
    # Uncomment the following lines to enable debug output
    # import logging
    # logging.basicConfig(level=logging.DEBUG)
    
    
    async def main():
    
        # Should look like:
        # https://XXXXXXXXXXXXXXXXXXXXXXXXXX.appsync-api.REGION.amazonaws.com/graphql
        url = os.environ.get("AWS_GRAPHQL_API_ENDPOINT")
        api_key = os.environ.get("AWS_GRAPHQL_API_KEY")
    
        if url is None or api_key is None:
            print("Missing environment variables")
            sys.exit()
    
        # Extract host from url
        host = str(urlparse(url).netloc)
    
        auth = AppSyncApiKeyAuthentication(host=host, api_key=api_key)
    
        transport = AIOHTTPTransport(url=url, auth=auth)
    
        async with Client(
            transport=transport, fetch_schema_from_transport=False,
        ) as session:
    
            query = gql(
                """
    mutation createMessage($message: String!) {
      createMessage(input: {message: $message}) {
        id
        message
        createdAt
      }
    }"""
            )
    
            variable_values = {"message": "Hello world!"}
    
            result = await session.execute(query, variable_values=variable_values)
            print(result)
    
    
    asyncio.run(main())