Blueprint: Invoke-NBGraphQL
Overview
PowerShell wrapper for NetBox's GraphQL API endpoint, enabling efficient complex queries with precise field selection and reduced over-fetching.
Research Summary
Based on testing against NetBox 4.4.7:
| Property | Value |
|---|---|
| Endpoint | /graphql/ (POST only) |
| Authentication | Token-based (same as REST) |
| Schema types | 378 types, 236 root queries |
| Mutations | None - GraphQL is read-only |
| Pagination | Offset-based (limit, offset) |
Function Signature
function Invoke-NBGraphQL {
[CmdletBinding()]
[OutputType([PSCustomObject])]
param(
# The GraphQL query string
[Parameter(Mandatory, Position = 0)]
[ValidateNotNullOrEmpty()]
[string]$Query,
# Variables to pass to the query (hashtable)
[Parameter()]
[hashtable]$Variables,
# Operation name (for queries with multiple operations)
[Parameter()]
[string]$OperationName,
# Return raw response including errors array
[Parameter()]
[switch]$Raw,
# Path to extract from response (e.g., "device_list")
[Parameter()]
[string]$ResultPath
)
}
Parameters
-Query (Mandatory)
The GraphQL query string. Supports:
- Simple queries: { device_list { id name } }
- Named queries: query GetDevices { ... }
- Parameterized queries: query GetDevices($limit: Int!) { ... }
-Variables
Hashtable of variables to substitute in the query. Automatically serialized to JSON.
-Variables @{ limit = 10; status = "STATUS_ACTIVE" }
-OperationName
Required when query contains multiple named operations.
-Raw
Returns the complete GraphQL response including:
- data - The query results
- errors - Array of any errors
Without -Raw, only the data property is returned (or throws on errors).
-ResultPath
Dot-notation path to extract specific data from the response.
# Instead of $result.data.device_list
Invoke-NBGraphQL -Query "{ device_list { id } }" -ResultPath "device_list"
Implementation Details
1. Request Construction
$body = @{
query = $Query
}
if ($Variables) {
$body.variables = $Variables
}
if ($OperationName) {
$body.operationName = $OperationName
}
$jsonBody = $body | ConvertTo-Json -Depth 10 -Compress
2. API Call
$uri = BuildNewURI -Segments @('graphql')
$response = InvokeNetboxRequest -URI $uri -Method POST -Body $jsonBody -Raw
Note: May need to bypass BuildURIComponents since GraphQL doesn't use query parameters.
3. Error Handling
GraphQL returns errors differently than REST:
{
"data": null,
"errors": [
{
"message": "Field 'invalid' not found",
"locations": [{ "line": 1, "column": 10 }]
}
]
}
Strategy:
- If errors present and -Raw not specified → throw terminating error
- If errors present and -Raw specified → return full response
- If data is null without errors → return $null
if ($response.errors -and -not $Raw) {
$errorMessages = ($response.errors | ForEach-Object { $_.message }) -join "; "
throw "GraphQL query failed: $errorMessages"
}
if ($Raw) {
return $response
}
$result = $response.data
if ($ResultPath) {
foreach ($segment in $ResultPath.Split('.')) {
$result = $result.$segment
}
}
return $result
4. Authentication Edge Case
Without authentication, NetBox returns HTML login page instead of JSON error. Must detect and handle gracefully:
# In InvokeNetboxRequest or here
if ($response -is [string] -and $response -match '<!DOCTYPE html>') {
throw "Authentication required. Use Connect-NBAPI first."
}
Usage Examples
Basic Query
# Simple query
Invoke-NBGraphQL -Query '{ device_list { id name status } }'
# With result path extraction
Invoke-NBGraphQL -Query '{ device_list { id name } }' -ResultPath 'device_list'
With Variables
$query = @'
query GetDevices($limit: Int!, $status: DeviceStatusEnum) {
device_list(
filters: { status: $status }
pagination: { limit: $limit }
) {
id
name
status
}
}
'@
Invoke-NBGraphQL -Query $query -Variables @{
limit = 10
status = "STATUS_ACTIVE"
} -ResultPath 'device_list'
Complex Nested Query
# Get all switches in a region with their IP addresses - single query!
$query = @'
{
device_list(filters: { role: { name: { exact: "switch" } } }) {
name
serial
site {
name
region { name }
}
primary_ip4 { address }
interfaces {
name
enabled
ip_addresses { address }
}
}
}
'@
$switches = Invoke-NBGraphQL -Query $query -ResultPath 'device_list'
Error Handling
# Get raw response including errors
$response = Invoke-NBGraphQL -Query '{ invalid_query }' -Raw
if ($response.errors) {
Write-Warning "Query had errors: $($response.errors.message -join ', ')"
}
# Or let it throw
try {
Invoke-NBGraphQL -Query '{ invalid_query }'
} catch {
Write-Error "GraphQL failed: $_"
}
Introspection
# Get available types
$schema = Invoke-NBGraphQL -Query '{
__schema {
types { name kind }
}
}' -ResultPath '__schema.types'
# Get fields for a specific type
$deviceFields = Invoke-NBGraphQL -Query '{
__type(name: "DeviceType") {
fields { name type { name } }
}
}' -ResultPath '__type.fields'
Helper Functions (Optional Future Scope)
Get-NBGraphQLSchema
function Get-NBGraphQLSchema {
# Returns introspection data for exploring available queries
}
ConvertTo-NBGraphQLFilter
function ConvertTo-NBGraphQLFilter {
# Converts PowerShell hashtable to GraphQL filter syntax
# Handles enum conversion (active → STATUS_ACTIVE)
}
Testing Strategy
Unit Tests
- Query string construction
- Variable serialization
- Error response parsing
- ResultPath extraction
- Parameter validation
Integration Tests
- Simple query execution
- Query with variables
- Nested data retrieval
- Error handling (invalid query)
- Authentication requirement
- Pagination
File Location
Functions/
└── Setup/
└── Invoke-NBGraphQL.ps1
Or alternatively in a new GraphQL folder:
Functions/
└── GraphQL/
└── Invoke-NBGraphQL.ps1
Dependencies
BuildNewURI- URI constructionInvokeNetboxRequest- Or directInvoke-RestMethodfor special handlingGet-NBCredential- Authentication tokenCheckNetboxIsConnected- Connection validation
Open Questions
- Location: Should this be in
Setup/(like Connect-NBAPI) or newGraphQL/folder? - Caching: Should we cache schema introspection results?
- Query validation: Should we validate queries against schema before sending?
- Enum helpers: Should we auto-convert
active→STATUS_ACTIVE?
Version Requirements
- NetBox: 3.0+ (GraphQL introduced in 3.0)
- PowerShell: 5.1+ (same as module)