It is all about the REST
Neo4j DB as a Service ( DBaaS ) , Aura, now has a RESTful like API for provisioning. The full details of the API in long form are in the documentation; in short it’s a set of endpoints that provide for CRUD operations. You can try the Aura API from the documentation. Here we’re going to dig in and write code to do the needful.
At the time of writing ( Friday 20th October ) the API is for Aura Enterprise customers and I’m reliably informed that the audience will be expanded in the next couple of months.
To show how the Aura API can be used, we’ll create a basic Python application that creates a new Aura instance.
We’ll walk through the various bits of Python code before putting it together. If you just want the code, jump to the bottom of this post where you can download it.
This is not a short read and you would be advised to have a bio break, get a hot beverage and settle in for some rough and ready coding.
To the Aura Console
To get started with the Aura API you’ll need to obtain a client id and client secret. BTW - I’m assuming that you already have an Aura Enterprise account.
After you have logged in , drive the mouse pointer to the top right of the Aura of the console and click on your account name
Then choose Account Details and then Create. This will bring up the dialog for a new client id and client secret. Make sure you copy these down and store them securely.
Armed with these two pieces of information we can now move onto the Aura API.
Let me see some ID
Aura API is protected against people with flexbile morals by a time limited OAuth token which is obtained by using the client id and client secret. OAuth is a well documented mechanism to use for this and covering that is beyond the confines of this blog post. Go Google it if you’re interested.
Once we have an OAuth token, and it’s only valid for a limited time, we can go ahead and use the Aura API.
Important You must send the OAuth token with every request that you make.
def get_oauth_token(aura_client_id,aura_client_secret):
"""
Returns oauth token for use with Aura API
params:
client_id , required, string client id obtained from Aura console
client_secret, required, string client secret obtained from Aura console
"""
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
data = {"grant_type": "client_credentials"}
url = "https://api.neo4j.io/oauth/token"
response = requests.request(
"POST",
url,
headers=headers,
data=data,
auth=HTTPBasicAuth(aura_client_id,aura_client_secret),
timeout=10
)
try:
response.raise_for_status()
except Exception as e:
print("Authentication request was not succesful.")
raise e
return response.json()["access_token"]
Now we can get the OAuth token, what’s next?
Endpoints , oh the endpoints
If you did take a look at the Aura documentation, you’ll notice that there’s several endpoints that available for various operations. For our purposes, we’ll need to use just two:-
- /tenants
- /instances
As we have two endpoints to use, it will be helpful to have a function that takes care of network communication otherwise we’ll be duplicating code. Also you can use this yourself , although you may want to make some improvements.
We’ll write a function that takes the Aura endpoint URL we want to talk to, the HTTP method, our oauth token and then return the response as a Python dictionary.
def call_endpoint(aura_endpoint, method, aura_token):
base_url = "https://api.neo4j.io/v1"
full_url = f"{base_url}{aura_endpoint}"
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {aura_token}"
}
response = requests.request(method, full_url, headers=headers, timeout=10)
try:
response.raise_for_status()
except Exception as e:
print("Request was not succesful.")
raise e
return response.json()
You can’t park there
Creating an instance requires a number of items to be given , one of which is the ID of the tenant for the instance. This makes sure we create the Aura instance in the right place. Could be awkward if they started turning up in random tenants within our Aura organisation; the explaining would be needed.
We’ll use the /tenants endpoint which returns all tenants with ID. With that we can look up the tenant ID based on the tenant name that we want to use. To find the tenant name , just look in the Aura console at top. Using the screenshot in this post, this would be ‘MyTenant’
Lets write a function to get the tenant list and return the id based on the tenant name.
def get_tenant_id(tenant_name,token):
"""
For a given tenant name, return it's id
params:
tenant_name, required, string, the name of the tenant to retrieve the id for
"""
aura_tenants = call_endpoint('/tenants', 'GET', token)
for entry in aura_tenants['data']:
if entry['name'] == tenant_name:
tenant_id = entry['id']
else:
tenant_id = 'Not found'
return tenant_id
Notice that we’re passing the token , something that we’ll need to do for every endpoint that we use
With the ID, we can now create the Aura instance.
You can guess what’s coming next
Yet another function
This will create an Aura instance to a pre-determined specification. We’ll need to pass the tenant name, oauth token and the name we want for our new instance.
To use the endpoint for creating an instance, we will need
- Instance name
- The cloud provider
- Cloud provider reqion to use
- Memory
- Tenant ID
- Type of Aura instance
- Neo4j version
Tenant ID will be obtained from get_tenant_id. Everything else will come from the supplied parameters or be set directly within the function.
def create_instance(aura_token, aura_tenant, aura_instance_name):
"""
Creates an Aura instance in the given tenant.
Aura instance will have 2Gb memory, 1 CPU and 4Gb of storage using Google Cloud in europe-west1
params:
aura_token, string , required, a valid oauth token for Aura
aura_tenant, string, the name of the tenant to create the instance in
"""
# Get the tenant_id
tenant_id = get_tenant_id(aura_tenant, aura_token)
if tenant_id != 'Not found':
spec = {
"version": "5",
"region": "europe-west1",
"memory": "2GB",
"name": aura_instance_name,
"type": "enterprise-db",
"tenant_id": tenant_id,
"cloud_provider": "gcp"
}
create_response = call_endpoint('/instances','POST',aura_token,data=json.dumps(spec))
return create_response
return
If everthing works, and why would it not, then the endpoint returns a JSON document with the details for our new instance. Our Python code translates this into a Python dictionary.
About time we put this together
Main event
Here’s the main function that calls the functions and prints out the result from creating a new instance
def main():
# Get an oauth token
aura_api_token = get_oauth_token('YOUR CLIENT ID', 'YOUR CLIENT SECRET')
# create the new instance
new_instance_response = create_instance(aura_api_token,'YOUR TENANT NAME','YOUR INSTANCE NAME')
#Print out the results
if 'data' in new_instance_response:
print(f"Instance ID {new_instance_response['data']['id']}")
print(f"Username {new_instance_response['data']['username']}")
print(f"Password {new_instance_response['data']['password']}")
print(f"Connection URI {new_instance_response['data']['connection_url']}")
else:
print('Bad things happened')
That’s it folks
The only thing left to do is to put all of this inside a Python application
You can copy and paste the functions after the imports but before the call to main(). Hint: There’s a big gap - put them there !
If you can’t be bothered then the entire Python code can be download : Create an new instance
from requests.auth import HTTPBasicAuth
import requests
import json
# Paste the functions from this point
# But before here
main()
If you’ve kept going to the end, I hope this has been useful to you
To demonstrate what can be done with the Aura API, we’ve built a CLI tool for Aura on top of it. You can download the Aura CLI from Neo4j Labs