By now, almost every device or system has a REST API. REST APIs are well understood and streamline getting data into and out of systems programmatically. Since REST APIs are resource-specific, there is a different API for every resource. Because of this paradigm, users tend not to understand the relationships between the data resources. And in order to get all of the data needed to perform an operation, users may need to make numerous API calls, such as fetching a resource, getting an index of the related resource, fetching that related resource and the index of yet another related object, and the process continues.
This post introduces GraphQL, a flexible API query language that streamlines working with relational data programmatically. You’ll get to see firsthand the value of GraphQL as we explore Nautobot’s GraphQL implementation. Having GraphQL as part of Nautobot makes it much simpler to gather data from your Network Source of Truth!
This is the first in a series of posts that will discuss:
A good user interface (UI) will present information in a way the user can understand. It does so by clearly presenting requested data points in a way easy for the user to understand, providing context for a data point, and allowing the user to quickly access related data points.
GraphQL is for programmatic data what a good UI is for a person
Take IP addresses as an example: a workflow may require retrieval of a single IP address for a given interface. Nautobot’s Web UI presents an interface’s IP address along with contextual information, including the remotely connected device and interface, as in the screenshot below:
Not only does this view provide info on the objects related to the Interface and IP address, it also provides links to the related objects. This contextual data in the UI helps people understand the larger relationship for the IP address and easily access related information.
The next section will explore how GraphQL provides the same clear presentation of requested data and contextual information, but in a programmatic format.
GraphQL is a query language for APIs. It allows a user to programmatically request and receive data from a server. One of GraphQL’s defining features is the capability to allow the user to specify the exact data needed, even if the data components are in different data silos. And, unlike REST API queries, a GraphQL query will return only the specific, requested data, even if the requested data spans multiple resources.
GraphQL benefits summary:
A single GraphQL query returns concise data would otherwise require multiple REST queries and extensive filtering
For example, imagine you wanted to retrieve the IP address for an interface. It may follow that your process will also need other information related to the interface IP address: interface name, remote interface name, remote IP address, and remote device. Using GraphQL, you can easily specify the exact data you need, including the required related data. This post will demonstrate how to get started with GraphQL queries via Nautobot’s GraphiQL interface.
Nautobot contains a GraphiQL interface. GraphiQL (notice the included i) is an interactive browser environment that allows users to easily craft GraphQL queries and explore the returned data.
The exercises in this article use the public Nautobot sandbox available at https://demo.nautobot.com. We encourage you to follow along as we explore Nautobot’s GraphiQL interface.
From the Nautobot Web UI homepage, scroll down and look to the lower right corner for a multi-choice menu. On that multi-choice menu, click on the GraphQL icon.
You’ve arrived at the GraphiQL Interface:
The GraphiQL interface for the public demo is https://demo.nautobot.com/graphql/.
Now that we’ve reached the GraphiQL UI, let’s explore how to leverage it.
One very interactive feature in GraphiQL is autocompletion. When the user constructs a query, the UI provides allowed completions for the level the user is typing on. This is possible because the system understands the GraphQL schema and relationships. In the example below, the user is typing within the query stanza, and the UI is showing allowed autocompletions that include the text the user has already typed:
Nautobot’s GraphiQL interactive environment enables the user to quickly create queries via access to the relevant data model in the UI. In GraphiQL, notice the <Docs button on the upper-right side. Clicking on this opens an interactive Documentation Explorer menu.
In the screenshot below, to see what options are available for query in devices, type out (1) devices, then (2) click on the [DeviceType] link (you may have to hover the cursor over devices for a moment to see the Query:devices:[DeviceType] link appear). This opens the DeviceType Explorer. In this example, the user explores the schema to find that (3) name is an allowed field to query within the devices stanza.
Quick access to Nautobot’s data models in real time gives the user the power to quickly create an effective query.
The following are all examples from Network to Code’s public Nautobot demo instance, which has a robust set of data to demo and practice against. We encourage you to follow along at https://demo.nautobot.com.
This first query will return all device names and only the device names: the data returned will not require any filtering. Start typing in the query on the left hand side. In the example below, Nautobot is showing a list of valid attributes that contain the letter ‘n’ for a device object. Once you’ve complete the query (1), press the play button (2) to execute and see the data (3):
Take specific notice that only the requested data about the devices (each device’s name) was returned. GraphQL returns a data set that does not require filtering out extraneous information. A subsequent post in this series will explore that capability further.
NOTE: Complete text examples for this and the remaining queries are at the end of this post
The next query returns the site name along with each device name. Notice the site attribute nests within the device object, in accordance with the Device data model.
Here is the full query and some of the returned data:
In the next scenario, the user wants to retrieve a list of device names, but only for devices in the ams
site. To narrow the returned data to a specific site, use a query parameter. In this case we will query for devices in the ams site using the (site: “ams”)
query parameter. Since the query is explicitly narrowing the data to the ams site, returning the device’s site name is no longer necessary.
This is a fairly simple query with the expected results. Notice that GraphQL returned only the requested data, saving us the time of filtering out unneeded data.
The next example will be a bit more sophisticated and start to show how GraphQL can return data that would otherwise require multiple REST queries.
GraphQL can return in one request data that would otherwise require multiple API requests. This next example shows a user requesting a list of interface names for each device in the ams
site and the IP address on each interface:
See the Text Query Examples and Results at the end of this post for a more complete example of the returned GraphQL data.
This GraphQL query returns a data structure that otherwise would require 12 REST queries and extensive post processing
This example humbly executes and demonstrates a powerful GraphQL capability in Nautobot. Namely:
https://demo.nautobot.com/api/dcim/devices?site=ams
https://demo.nautobot.com/api/dcim/interfaces?site=ams
https://demo.nautobot.com/api/ipam/ip-addresses?device=ams-edge-01
https://demo.nautobot.com/api/dcim/interfaces?site=ams
API, which returns data for all interfaces and allows site to be specified as a query parameterA follow-on post in this series will explore in-depth those required REST calls and the subsequent Python data processing that would produce results equivalent to this single GraphQL query.
To finish out our exploration of the ams
site, the user also needs the interface’s remote device and connected interface:
When constructing sophisticated queries like this one, it is very helpful to leverage the autocomplete feature in the query. This feature will show you if you are in the correct position in the data model to get the data point you are looking for (connected_interface
autocompletes in the interfaces
stanza) . . .
. . . or not! Notice that connected_interface
is not an option and will not autocomplete in the devices
stanza:
Earlier, this article showed Nautobot’s Web UI displaying the IP address (10.0.192.0/32
) for Interface Ethernet1/1
on atl-edge-01
. That same page also showed the connected Interface (Ethernet1/1
) on the connected device (atl-edge-02
).
GraphQL’s ease of querying and concise data retrieval allow both automation and people to efficiently retrieve and process data
The GraphQL equivalent query and results in GraphiQL are:
This last example showcases two query parameters that qualify the interfaces
query:
device:“atl-edge-01”
name__ie
lookup expression
name:"Ethernet1/1
instead, but would require exact capitalization matchingMost optimizations have trade-offs, and GraphQL is no exception. By returning only the requested data that may span multiple resources, GraphQL offloads much of the data processing from the user onto the server. Depending on your situation and use case, this may or may not be the desired behavior.
In terms of performance, GraphQL is best suited for
In general, it is best to use multiple GraphQL queries or multiple individual REST API queries for
The next post in this series will go on to demonstrate how to retrieve GraphQL data programmatically in Postman and in Python. Another post will use the fourth example in this post as a case study to examine the required REST API calls and processing the user would have to perform if using REST API queries instead of GraphQL.
-Tim
The text of the GraphQL queries and the results for each are below. The GraphQL queries can be pasted into a GraphiQL query or used in the body of an API call.
Query Nautobot for all device names
query {
devices {
name
}
}
Result (abbreviated for brevity)
{
"data": {
"devices": [
{
"name": "ams-edge-01"
},
{
"name": "ams-edge-02"
},
{
"name": "ams-leaf-01"
},
…
…
{
"name": "sin-leaf-07"
},
{
"name": "sin-leaf-08"
}
]
}
}
query {
devices {
name
site {
name
}
}
}
Here is the returned data, snipped for brevity:
{
"data": {
"devices": [
{
"name": "ams-edge-01",
"site": {
"name": "ams"
}
},
{
"name": "ams-edge-02",
"site": {
"name": "ams"
}
},
...
...
{
"name": "sin-leaf-07",
"site": {
"name": "sin"
}
},
{
"name": "sin-leaf-08",
"site": {
"name": "sin"
}
}
]
}
}
Query Nautobot for the names of all the devices in the ams
site:
query {
devices(site:"ams") {
name
}
}
Here is the sample output, snipped for brevity:
{
"data": {
"devices": [
{
"name": "ams-edge-01"
},
{
"name": "ams-edge-02"
},
{
"name": "ams-leaf-01"
},
...
...
{
"name": "ams-leaf-07"
},
{
"name": "ams-leaf-08"
}
]
}
}
query {
devices(site:"ams") {
name
interfaces {
name
ip_addresses {
address
}
}
}
}
Abbreviated results:
{
"data": {
"devices": [
{
"name": "ams-edge-01",
"interfaces": [
{
"name": "Ethernet1/1",
"ip_addresses": [
{
"address": "10.11.192.0/32"
}
]
},
{
"name": "Ethernet2/1",
"ip_addresses": [
{
"address": "10.11.192.2/32"
}
]
},
...
...
{
"name": "ams-leaf-08",
"interfaces": [
{
"name": "Ethernet1",
"ip_addresses": [
{
"address": "10.11.192.33/32"
}
]
},
...
...
{
"name": "vlan99",
"ip_addresses": [
{
"address": "10.11.71.0/32"
}
]
},
{
"name": "vlan1000",
"ip_addresses": [
{
"address": "10.11.7.0/32"
}
]
}
]
}
]
}
}
query {
devices(site:"ams") {
name
interfaces {
name
ip_addresses {
address
}
connected_interface {
device {
name
}
name
}
}
}
}
Abbreviated results:
{
"data": {
"devices": [
{
"name": "ams-edge-01",
"interfaces": [
{
"name": "Ethernet1/1",
"ip_addresses": [
{
"address": "10.11.192.0/32"
}
],
"connected_interface": {
"device": {
"name": "ams-edge-02"
},
"name": "Ethernet1/1"
}
},
{
"name": "Ethernet2/1",
"ip_addresses": [
{
"address": "10.11.192.2/32"
}
],
"connected_interface": {
"device": {
"name": "ams-edge-02"
},
"name": "Ethernet2/1"
}
},
...
...
{
"name": "Management1",
"ip_addresses": [],
"connected_interface": null
}
]
},
...
...
{
"name": "ams-leaf-08",
"interfaces": [
{
"name": "Ethernet1",
"ip_addresses": [
{
"address": "10.11.192.33/32"
}
],
"connected_interface": {
"device": {
"name": "ams-edge-01"
},
"name": "Ethernet10/1"
}
},
{
"name": "Ethernet2",
"ip_addresses": [
{
"address": "10.11.192.35/32"
}
],
"connected_interface": {
"device": {
"name": "ams-edge-02"
},
"name": "Ethernet10/1"
}
},
...
...
{
"name": "vlan99",
"ip_addresses": [
{
"address": "10.11.71.0/32"
}
],
"connected_interface": null
},
{
"name": "vlan1000",
"ip_addresses": [
{
"address": "10.11.7.0/32"
}
],
"connected_interface": null
}
]
}
]
}
}
query {
interfaces(device:"atl-edge-01", name__ie:"Ethernet1/1") {
name
ip_addresses {
address
}
connected_interface {
name
device {
name
}
}
}
}
Result:
{
"data": {
"interfaces": [
{
"name": "Ethernet1/1",
"ip_addresses": [
{
"address": "10.0.192.0/32"
}
],
"connected_interface": {
"name": "Ethernet1/1",
"device": {
"name": "atl-edge-02"
}
}
}
]
}
}
Share details about yourself & someone from our team will reach out to you ASAP!