Sync Customers Programmatically
When you embed Prismatic into your app, you need to authenticate your customer users as users of specific customers in Prismatic. You can establish these customer records in one of two ways:
- Create them programmatically through the API.
When authenticating embedded customer users, ensure that your customer record's
externalIdmatches your embedded JWT'scustomerclaim. - Specify both
externalIdandnameas embedded JWT claims. With this approach, customer records will be automatically created if they don't already exist.
This guide focuses on option 1, demonstrating how to programmatically create and manage customer records rather than relying on automatic creation from your embedded application. You'll use External IDs, which provide a way to link your external customer identifiers with Prismatic customer records.
Setting up
This tutorial uses Python, though you can adapt the same ideas to any language that has a GraphQL client library. Full code shown in this tutorial can be found on GitHub.
To start, add gql to your dependencies for your Python project:
pip install gql
Next, import the necessary libraries and create a GraphQL client so you can run queries against Prismatic's API. Define the transport layer for getting to the API (HTTP), which will include an authorization header to authenticate your client against Prismatic:
import json
import os
import sys
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
token = os.environ['PRISMATIC_API_KEY']
api_endpoint = "https://app.prismatic.io/api/"
transport = RequestsHTTPTransport(
url=api_endpoint,
headers={'Authorization': f'Bearer {token}'}
)
client = Client(transport=transport)
This code assumes that an environment variable, PRISMATIC_API_KEY, has been set (it's best practice not to hard-code API keys).
To get an API key into your environment variables, you can use the me:token prism subcommand:
export PRISMATIC_API_KEY=$(prism me:token)
Now that you have a working client, you can write some GraphQL queries and mutations to manage customers within Prismatic:
GraphQL queries and mutations
To sync customers, you'll use the customers query and createCustomer and deleteCustomer mutations.
In GraphQL lingo, a query is similar to a SELECT statement in SQL.
You query for a particular set of information in a read-only way.
A mutation is similar to an INSERT, UPDATE, or DELETE statement in SQL.
You mutate data by creating new records, or updating or deleting existing records.
List all customers
First, write a function that gets a list of all your customers, including their name, description, Prismatic id, and externalId:
def getCustomers():
query = gql("""
query {
customers {
nodes {
id
name
description
externalId
}
}
}
""")
result = client.execute(query)
return result['customers']['nodes']
This function runs a customers query against Prismatic's API.
The query returns an object with a customers key, which has (in GraphQL lingo) has a series of nodes (customers).
The function returns result['customers']['nodes'] so that just a list of customer objects are returned.
Invoke this function and use json.dumps() for readability:
print(json.dumps(getCustomers()))
[
{
"id": "Q3VzdG9tZXI6MThjZTBjM2EtYmQ5NS00MWJiLWIyMjUtN2MwYjVjMDg3YmE4",
"name": "Mars Missions",
"description": "Mars Missions Corp",
"externalId": "abc-123"
},
{
"id": "Q3VzdG9tZXI6MDllZDQyNTctMTNkMS00YTY4LWFiNTktY2Y5NzNmZGUyOTQy",
"name": "Eastern Space Flight",
"description": "Eastern Space Flight - Houston, TX",
"externalId": "xyz-456"
}
]
You can use the output of this function to figure out which customers have not been synced into Prismatic, or to sync Prismatic customers back to an external system.
GraphQL pagination
Before moving on, let's address pagination. By default, the Prismatic API returns the first 100 results for a query. So, the customers query only returns 100 customers, even if you have more than 100 in the system.
If you want to download all customers, you'll need to update your getCustomers() function a bit.
Update your query so that it requests pagination information (pageInfo).
Request whether or not there are additional pages to fetch (hasNextPage), and a unique ID indicating where to start the next page (endCursor).
Feed that endCursor back in to your query as a parameter after, so the GraphQL API knows where it last left off:
def getCustomers():
query = gql("""
query ($startCursor: String){
customers(after: $startCursor) {
nodes {
id
name
description
externalId
}
pageInfo {
hasNextPage
endCursor
}
}
}
""")
cursor = ""
hasNextPage = True
customers = [] # Used to accumulate customer objects
while hasNextPage:
result = client.execute(query, variable_values={"startCursor": cursor})
customers += result['customers']['nodes']
hasNextPage = result['customers']['pageInfo']['hasNextPage']
cursor = result['customers']['pageInfo']['endCursor']
return customers
Fetch a specific customer by external ID
Now, you can look up a specific customer by their externalId.
The customers query allows you to filter customers by externalId.
The function passes in the externalId as a variable to the customers query using an object, params, that contains the GraphQL variables to use.
The function also asserts that exactly one customer object is returned from the API:
def getCustomerByExternalId(externalId):
query = gql("""
query ($externalId: String!) {
customers (externalId: $externalId) {
nodes {
id
name
description
externalId
}
}
}
""")
params = {"externalId": externalId}
response = client.execute(query, variable_values=params)
assert len(response["customers"]["nodes"]) == 1, f"No customer with external ID '{externalId}' exists."
return response["customers"]["nodes"][0]
If you invoke this function now, you'll get a single customer based on the externalId that you provide:
print(json.dumps(getCustomerByExternalId("abc-123")))
{
"id": "Q3VzdG9tZXI6MThjZTBjM2EtYmQ5NS00MWJiLWIyMjUtN2MwYjVjMDg3YmE4",
"name": "Mars Missions",
"description": "Mars Missions Corp",
"externalId": "abc-123"
}
Create a new customer
Next, write a function that creates a new customer given the customer's name, description, and externalId.
To do that, use the createCustomer GraphQL mutation.
Similar to the getCustomerByExternalId function above, supply some input variables to your mutation with a params object:
def createCustomer(name, description="", externalId=""):
mutation = gql("""
mutation($name: String!, $description: String, $externalId: String) {
createCustomer(
input: { name: $name, description: $description, externalId: $externalId }
) {
customer {
id
name
description
externalId
}
errors {
messages
}
}
}
""")
params = {
"name": name,
"description": description,
"externalId": externalId
}
result = client.execute(mutation, variable_values=params)
if result["createCustomer"]["errors"]:
raise Exception(result["createCustomer"]["errors"])
else:
return result["createCustomer"]["customer"]
The createCustomer mutation will return errors if the name or externalId you specified is already in the Prismatic system.
If the mutation does not throw an error, it will return a customer object containing the new customer's name, description, externalId and generated Prismatic id.
Let's try it out:
print(
json.dumps(
createCustomer(
name="Rockets Rockets Rockets",
description="Rockets^3",
externalId="456-xyz"
)))
{
"id": "Q3VzdG9tZXI6NGQ3ZDc3ZTktOTllNy00NmJiLWFlNDktMTg1N2JlNWNiYjUz",
"name": "Rockets Rockets Rockets",
"description": "Rockets^3",
"externalId": "456-xyz"
}
Delete a customer by external ID
For the last customer-related function, delete a customer given their external ID.
To do that, use the deleteCustomer mutation.
First, the function looks up the customer to delete using the getCustomerByExternalId function written previously.
Then, the function feeds the Prismatic ID into the deleteCustomer mutation:
def deleteCustomer(externalId):
customer = getCustomerByExternalId(externalId)
mutation = gql("""
mutation ($id: ID!) {
deleteCustomer(
input: {
id: $id
}
) {
customer {
id
name
description
externalId
}
errors {
messages
}
}
}
""")
params = {"id": customer["id"]}
result = client.execute(mutation, variable_values=params)
if result["deleteCustomer"]["errors"]:
raise Exception(result["deleteCustomer"]["errors"])
else:
return result["deleteCustomer"]["customer"]
If you supply an external ID that is not attached to a customer, the getCustomerByExternalId() function will throw an error indicating that.