-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
GraphQL support #2007
Comments
I'd also like to explore this at some point, but I have to close this out as our current policy does not allow for any new major feature requests until the backlog of existing requests has been worked through. |
We've started an integration of GraphQL into Netbox a while back. It worked great for all implemented queries. Unfortunately we can't maintain it anymore, because we never got to use it. But it might still serve as reference should someone else ever see the huge benefit over static REST APIs and decide to either maintain the project in a fork or integrate it into Netbox. |
@jeremystretch at what point is it possible to reopen this issue? |
Re-opening this as a candidate for v3.0. |
I think MVP release for GraphQL should focus on READ / GET only since that is where most people have headaches with REST / post GET data manipulations. Sure there would be some interesting write/updates using GraphQL, but I am doubtful the ROI is as high. It also allows you to iterate on the integration with a limited scope. |
Agree with ryanmerolle. Biggest gains are by far the GET requests. |
There are some important things which are impossible via REST at the moment, and graphQL might be able to offer a solution. In particular: add a device (or VM) + interface + primary IP address in one transaction. Currently there's a minimum of 3 creates plus 1 update to achieve this. |
Yes, and it makes perfect sense to keep those as discrete actions, because each is doing something very different. Each object has its own validation logic and implications. Trying to bundle them all together increases the time and memory needed to perform the request. It also greatly complicates the reporting of errors, sending webhooks, etc. |
It's also worth pointing out that creating ten devices with a few dozen interfaces each and an IP address on each interface via the REST API also takes four total requests. |
I've been experimenting with this for the past few days, and I'm happy to say I've made a surprising amount of progress in a fairly short time. The traditional approach to implementing GraphQL in a Django application requires a fair bit of overhead in defining object types, much of which is redundant to what we've already done with Django REST framework for the REST API. To help reduce developer burden and help ensure long-term maintainability, I've oped to employ graph_wrap by @PaulGilmartin to leverage our DRF serializers to serve GraphQL requests. Paul has been extremely helpful in working through some initial bumps, and I've already got a functional PoC going. As you're likely aware, NetBox today answers REST API requests in one of two modes: brief and normal. A brief response includes only the bare minimum representation of each obect (typically its ID, name, and a user-friendly display string), whereas the normal response includes a complete representation, exclusive of downstream related objects. For example, a rack will show its assigned site, but not associated devices within the rack. graph_wrap, however, requires a complete serialization of each object to support the inclusion of downstream related objects. I can think of two approaches to meeting this requirement:
The first approach may or may not be practical, given the intricacies of serializer initialization, however its at least worth exploring briefly. The second approach I have already confirmed as a potential solution; the only downside is needing to declare separate serializer classes. However, this may actually be more manageable in the long term, and easier to test. There's an added benefit of either approach: it will potentially enable "full" REST API requests. For example, a request to Just wanted to get my thoughts down for now. Will provide further updates when I have more to share. |
@jeremystretch Happy to help! If you get time, I'd be interested if you could be a bit more explicit about this statement "graph_wrap, however, requires a complete serialization of each object to support the inclusion of downstream related objects.". Perhaps you could provide an example of the kind of code you're talking about? |
@PaulGilmartin sure thing! I probably shouldn't have said graph_wrap, specifically, but rather GraphQL in general, requires this. Here's an example. In NetBox, we model circuits and providers; every circuit has a provider, and a provider may have one or more circuits. Here's what our ProviderSerializer current looks like: netbox/netbox/circuits/api/serializers.py Lines 19 to 28 in 7444110
You'll notice that we include a count of child circuits with the provider, but not the circuits themselves. This precludes GraphQL from querying related circuits. To support GraphQL (via graph_wrap), we need to extend our serializers to incorporate child objects. In this example, we need to add something like class ProviderSerializer:
...
circuits = CircuitSerializer(many=True, read_only=True) and update I find myself leaning more toward the second option, if only because working with a single serializer per model is going to make it very difficult (likely impossible) to avoid cyclical imports. For example, this obviously won't work: class ProviderSerializer:
circuits = CircuitSerializer(many=True)
class CircuitSerializer:
provider = ProviderSerializer() But creating a new "full" serializer by subclassing the existing serializer should: class ProviderFullSerializer(ProviderSerializer):
circuits = CircuitSerializer(many=True)
class CircuitSerializer:
provider = ProviderFullSerializer() |
Thinking this through a bit further, I think we'll run into an issue in the reverse direction: traversing upstream related objects. For example: Interface -> Device -> Rack -> Site -> Region -> Region -> Region. It may just not be worth pursuing the parallel to DRF any further, owing to the complexity of our data model. |
@jeremystretch Perhaps it's getting a bit late here and I'm not completely following, but I believe the problem you're describing is very similar to the one I discuss here https://github.com/PaulGilmartin/graph_wrap#which-problems-does-graphwrap-address. If so, it shouldn't be the case that you need to embed the
graph_wrap should be smart enough to get the circuits_type purely from a HyperlinkedIdentityField (as we describe in the link above with the Authors/Posts example). So you could have something like
and that should give you a full representation of the nested circuits object via /graphql but not via the REST endpoint (providing circuit-detail does point to some sort of appropriate CircuitSerializier). Have you tried this at all? |
I did give that a shot early on, but it seems to just load the related objects as strings:
Even if it was able to generate the serializer dynamically, I'm not sure we'd want to give up the control over the displayed object. |
Not sure why that error came about, but it wouldn't be generating a serializer dynamically. What it does is fetches the serializer associated to the view_name. So for example if we wrote
then the view_name 'circuit-detail' must be associated to a pre-defined I'd be happy to look at the code which gave the error you mentioned if you want to explore this further. |
Ah, I see. For whatever reason that doesn't seem to be working at the moment. Ok, it may be worth coming back to that. However, I think the bigger issue is figuring out how to navigate upstream relationships. Consider this model:
Our current InterfaceSerializer defines its |
@jeremystretch My first point would be that to support GraphQL, we don't necessarily need to have available full representations of every object to the client. GraphQL can still be of great use when only the limited scope of nested objects is ever exposed (as you do with NestedXXXSerializers). If however your aim of using GraphQL is to be able to expose full representations of nested objects, then I think doing something as I described above wouldn't be too much effort. Let's continue with the interface-device example above.
So a request to fetch an Interface would give JSON of the form
Now, I see in the codebase that what I think you describe as a "full" serializer already exists for Device:
What we want :
My claim is that this is possible, provided your
then graph_wrap should (i.e. it's a bug if it can't) be able to fetch the full representation of the nested device just from the fact the device url is exposed. Importantly, graph_wrap would make use of the existing So what if |
Agreed, however I think the expectation from our user base is the ability to fully traverse these relationships.
Just confirming that this is a correct assumption.
This is very helpful to know, thanks! I'm going to continue experimenting with this a bit more as time allows and see what I can work out. Thanks again for all your help! |
After a lot more exploration, I've decided to push forward just using graphene_django for this implementation. I have a very rough PoC going int the It would be extremely helpful if some of the folks who have expressed interested in GraphQL would take this opportunity to help test. As with every major release, we'll hold a beta period, but the sooner we can act on proposed modifications to the implementation, the better off we'll be. |
One consideration that's come up is whether we should namespace the GraphQL entry points with the application name for each model: for example, I don't have a strong preference either way, though I want to avoid needing to substantially redefine the GraphQL schema in a future release. Curious to hear any opinions on the matter. |
Another issue I've run into: Initially, I planned to use the singular form of a model's verbose name to fetch a single object, and the plural form to fetch a list (e.g. We can work around this by perhaps appending |
Are you referring to how naming is represented in the schema, or internally within netbox? If external/schema, I wouldn't grasp the need to expose singles. If a single site is requested it can be signaled in the qcl query?
|
@sdktr I think there's a lot of value in providing an interface to grab specific objects, just as with the REST API. It covers the (fairly frequent, I'd expect) case where the client expects to receive exactly one object, and that zero or 2+ objects should result in an error. It saves the client from having to implement that logic locally. |
Closes #2007: Implement GraphQL API
I ended up just appending |
Issue type
[X] Feature request
[ ] Bug report
[ ] Documentation
Environment
Description
I've talked about it a while back, but it came up in a different context in the networktocode#netbox slack channel recently.
We've implemented a Netbox integration for GraphQL a while back, but we're not able to maintain it at the moment. It's called django-netbox-graphql. Right now, it's built as a django module that can be plugged into Netbox quite easily. So it should be straight-forward to get going.
Some features:
Whats the case for GraphQL?
GraphQL is a "new" way to consume from a remote system. In contrast to REST, where there is a route for every resource, GraphQL only knows one route. The request to that route contains a query in a well-defined format. In the query the requesting application specifies which resources it would like to consume and, and this is essential, which objects it would like to have nested.
A short example
I would like to get all IPs of pool subnets and their corresponding MAC addresses to write them into a DHCP config file.
With REST
interface
object. But I also get a lot of unrelated information.With GraphQL
I would have to write one query that returns exactly the fields I need. It would look like this:
(Note: This query does not work yet with django-netbox-graphql!)
Conclusion
I understand, that this feature is no priority for Netbox. Also, the Netbox REST API is very powerful and already nests a lot of important information. But I also think that people want Netbox to become their single-source-of-truth for all kinds of information related to devices on their network. A versatile query language, that offers full flexibility and reduces requests to Netbox might be required in such a future.
The text was updated successfully, but these errors were encountered: