How to call an AppSync mutation with Cognito authentication using python?
Question:
Is it possible to calling an AppSync mutation with Cognito authentication using Python? How?
I am trying to use boto3, but I don’t found a way to execute graphql operations.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appsync.html
Answers:
You can turn your API auth mode to be “API KEY” and call an AppSync mutation with http.
For example.
import requests
import json
APPSYNC_API_KEY = 'da2-xxxxxxxxxxxxx'
APPSYNC_API_ENDPOINT_URL = 'https://xxxxxxxxxxxxx.appsync-api.us-west-2.amazonaws.com/graphql'
headers = {
'Content-Type': "application/graphql",
'x-api-key': APPSYNC_API_KEY,
'cache-control': "no-cache",
}
def execute_gql(query):
payload_obj = {"query": query}
payload = json.dumps(payload_obj)
response = requests.request("POST", APPSYNC_API_ENDPOINT_URL, data=payload, headers=headers)
return response
Imagine you have a model called Items
and you can easily make query like below:
if __name__ == '__main__':
print(execute_gql("query { listItems { items { id name } } }").json())
Simply replace the string with the mutation operation.
graphql-python/gql supports AWS AppSync since version 3.0.0rc0.
It supports queries, mutation and even subscriptions on the realtime endpoint.
It supports IAM, api key and JWT (for Cognito for example) authentication methods.
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())
It appears that idToken from auth response is the only one you need to pass under the Authorization
header. Nothing more is required. Auth middleware shouldn’t be passed in the Transport object at all. Here is how it finally worked from my side:
import boto3
from gql import gql, Client as GQLClient
from gql.transport.requests import RequestsHTTPTransport
username = "YOUR_USER_NAME"
password = "YOUR_PASSWORD"
authClientId = "YOUR_COGNITO_AUTH_APP_CLIENT_ID"
regionName = "YOUR_REGION_NAME"
appSyncEndpoint = "YOUR_APPSYNC_ENDPOINT"
def authenticate():
cgClient = boto3.client("cognito-idp", region_name=regionName)
response = cgClient.initiate_auth(
ClientId=authClientId,
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": username, "PASSWORD": password},
)
return response["AuthenticationResult"]["IdToken"]
def make_client(idToken):
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': idToken #<<<< Just an idToken
}
transport = RequestsHTTPTransport(url=appSyncEndpoint,
headers=headers) #<<<< there is no `auth` parameter here at all
client = GQLClient(transport=transport,
fetch_schema_from_transport=False)
return client
creds = authenticate()
queryText = """mutation createMessage($message: String!) {
createMessage(input: {message: $message}) {
id
message
createdAt
}
}"""
asClient = make_client(creds)
result = asClient.execute(gql(queryText))
print(result)
Is it possible to calling an AppSync mutation with Cognito authentication using Python? How?
I am trying to use boto3, but I don’t found a way to execute graphql operations.
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/appsync.html
You can turn your API auth mode to be “API KEY” and call an AppSync mutation with http.
For example.
import requests
import json
APPSYNC_API_KEY = 'da2-xxxxxxxxxxxxx'
APPSYNC_API_ENDPOINT_URL = 'https://xxxxxxxxxxxxx.appsync-api.us-west-2.amazonaws.com/graphql'
headers = {
'Content-Type': "application/graphql",
'x-api-key': APPSYNC_API_KEY,
'cache-control': "no-cache",
}
def execute_gql(query):
payload_obj = {"query": query}
payload = json.dumps(payload_obj)
response = requests.request("POST", APPSYNC_API_ENDPOINT_URL, data=payload, headers=headers)
return response
Imagine you have a model called Items
and you can easily make query like below:
if __name__ == '__main__':
print(execute_gql("query { listItems { items { id name } } }").json())
Simply replace the string with the mutation operation.
graphql-python/gql supports AWS AppSync since version 3.0.0rc0.
It supports queries, mutation and even subscriptions on the realtime endpoint.
It supports IAM, api key and JWT (for Cognito for example) authentication methods.
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())
It appears that idToken from auth response is the only one you need to pass under the Authorization
header. Nothing more is required. Auth middleware shouldn’t be passed in the Transport object at all. Here is how it finally worked from my side:
import boto3
from gql import gql, Client as GQLClient
from gql.transport.requests import RequestsHTTPTransport
username = "YOUR_USER_NAME"
password = "YOUR_PASSWORD"
authClientId = "YOUR_COGNITO_AUTH_APP_CLIENT_ID"
regionName = "YOUR_REGION_NAME"
appSyncEndpoint = "YOUR_APPSYNC_ENDPOINT"
def authenticate():
cgClient = boto3.client("cognito-idp", region_name=regionName)
response = cgClient.initiate_auth(
ClientId=authClientId,
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": username, "PASSWORD": password},
)
return response["AuthenticationResult"]["IdToken"]
def make_client(idToken):
headers = {
'Accept': 'application/json',
'Content-Type': 'application/json',
'Authorization': idToken #<<<< Just an idToken
}
transport = RequestsHTTPTransport(url=appSyncEndpoint,
headers=headers) #<<<< there is no `auth` parameter here at all
client = GQLClient(transport=transport,
fetch_schema_from_transport=False)
return client
creds = authenticate()
queryText = """mutation createMessage($message: String!) {
createMessage(input: {message: $message}) {
id
message
createdAt
}
}"""
asClient = make_client(creds)
result = asClient.execute(gql(queryText))
print(result)