graphql-api.md 5.4 KB

GraphQL API

Defining the Schema Class

A plugin can extend NetBox's GraphQL API by registering its own schema class. By default, NetBox will attempt to import graphql.schema from the plugin, if it exists. This path can be overridden by defining graphql_schema on the PluginConfig instance as the dotted path to the desired Python class.

Example

# graphql.py
import strawberry
import strawberry_django

from . import models


@strawberry_django.type(
    models.MyModel,
    fields='__all__',
)
class MyModelType:
    pass


@strawberry.type
class MyQuery:
    @strawberry.field
    def mymodel(self, id: int) -> MyModelType:
        return None
    mymodel_list: list[MyModelType] = strawberry_django.field()


schema = [
    MyQuery,
]

Extending Core Types & Filters

!!! info "This feature was introduced in NetBox v4.6."

In addition to registering its own top-level query fields, a plugin can inject fields and filters onto NetBox's existing core GraphQL types (e.g. DeviceType). This allows a plugin's related data to be traversed within a single query rooted at a core object, rather than requiring a separate top-level query. This mirrors the PluginTemplateExtension mechanism used to extend core object views in the UI.

An extension is a mixin class declaring a models attribute: a list of the lowercased app_label.model labels of the core types it extends. Output-type extensions are collected from graphql.type_extensions and filter extensions from graphql.filter_extensions by default; these paths can be overridden via the graphql_type_extensions and graphql_filter_extensions attributes on the PluginConfig.

Each declared path must resolve to a list named type_extensions (or filter_extensions) - for example, defined in graphql.py alongside the schema, or re-exported from the plugin's graphql package.

!!! warning

Do not import core GraphQL modules (e.g. `dcim.graphql.types`) from a plugin's `ready()`. Doing so assembles the affected core types before other plugins have registered their extensions, which are then silently dropped. A warning is logged under `netbox.graphql` if this occurs.

Type Extensions

An output-type extension is a @strawberry.type class whose fields and resolvers are spliced into the target type:

# graphql.py (or graphql/type_extensions.py)
from typing import Annotated

import strawberry
import strawberry_django

from utilities.querysets import RestrictedPrefetch
from my_plugin.models import Widget


@strawberry.type
class DeviceTypeExtension:
    models = ['dcim.device']

    @strawberry_django.field(
        prefetch_related=lambda info: RestrictedPrefetch(
            'widgets', info.context.request.user, 'view', queryset=Widget.objects.all()
        ),
    )
    def widgets(self) -> list[Annotated['WidgetType', strawberry.lazy('my_plugin.graphql.types')]]:
        return self.widgets.all()


type_extensions = [
    DeviceTypeExtension,
]

!!! note

Scope any related-object resolver with `RestrictedPrefetch(..., info.context.request.user, 'view', ...)`, as shown above. Object permissions are only applied to the top-level queryset, so a plain `prefetch_related='widgets'` returns related objects the requesting user may not be permitted to see.

Filter Extensions

A filter extension is a @strawberry.type class declaring additional filters - either as annotated filter fields or as custom filter methods - which are spliced into the target filter:

# graphql.py (or graphql/filter_extensions.py)
import strawberry
import strawberry_django
from django.db.models import Q


@strawberry.type
class DeviceFilterExtension:
    models = ['dcim.device']

    @strawberry_django.filter_field()
    def has_widgets(self, value: bool, prefix) -> Q:
        return Q(**{f'{prefix}widgets__isnull': not value})


filter_extensions = [
    DeviceFilterExtension,
]

With both registered, a client can fetch a device and its plugin-provided data in a single query:

query {
  device_list(filters: { has_widgets: true }) {
    name
    widgets { id name }
  }
}

!!! note

Extensions are strictly additive: they can only add new fields, never replace existing ones. If an extension declares a name the core type already provides, the core definition always takes precedence and the extension's version is ignored. If two extensions on the same type declare the same new name, the one whose plugin is loaded first (earlier in `PLUGINS`) wins. Both cases are logged as warnings under the `netbox.graphql` logger.

GraphQL Objects

NetBox provides two object type classes for use by plugins.

::: netbox.graphql.types.BaseObjectType

options:
  members: false

::: netbox.graphql.types.NetBoxObjectType

options:
  members: false

GraphQL Filters

NetBox provides a base filter class for use by plugins which employ subclasseses of NetBoxModel.

::: netbox.graphql.filters.NetBoxModelFilter

options:
  members: false

Additionally, the following filter classes are available for subclasses of standard base models.

Model Class FilterSet Class
PrimaryModel netbox.graphql.filters.PrimaryModelFilter
OrganizationalModel netbox.graphql.filters.OrganizationalModelFilter
NestedGroupModel netbox.graphql.filters.NestedGroupModelFilter