I started moving my REST API endpoints to using GraphQL with Graphene. Seems pretty straightforward so far, but one of the things that I like about the REST API (and I cannot figure out in Graphene) is the structure of "endpoints" for each app. I have a lot of apps in my Django application, and I would like to group the Graphene queries and mutations of each app under a single "endpoint" (just like you would do in REST by sending a request to app_1/endpoint
and app_2/endpoint
).
Currently I have a graphql
folder inside of each app, with files for my queries and mutations inside. Then, under my main schema file, I just create a giant query
and mutation
objects that inherit from the elements of all other apps.
# app1/graphql/queries.py
class Endpoint1(DjangoObjectType):
class Meta:
model = Element1
fields = ("id", "name", "date")
# app2/graphql/queries.py
class Endpoint2(DjangoObjectType):
class Meta:
model = Element2
fields = ("id", "name", "date")
# Place where my main schema is located
# django_project/graphql/queries.py
class Queries(Endpoint1, Endpoint2):
pass
Would it be possible to group queries and mutations from a single app and then just inherit from each of the app’s mutations and queries in the main schema, and then have the GraphQL request be structured like this?
query {
app1 {
endpoint1 {
id
name
}
}
}
query {
app2 {
endpoint2 {
id
name
}
}
}
With my current approach I currently just get all of the endpoints bunched up into a single set found under query.
query {
endpoint1 {
id
name
}
endpoint2 {
id
name
}
}
7
1 Answer
So, what I did was not go with federation (as I wanted a simple unified API and federation seemed a bit overkill to me), but to simply split the location of schemas in a per-app basis and simply join them in a common query found in the main Django project folder. Here’s an example:
This is for a file found in django_project/graphql/schema.py
from graphene import ObjectType, Field, Schema
from graphene_federation import key, build_schema
from app_config_generator.graphql.schema import ConfigGeneratorQuery
# ============================================== #
# QUERIES #
# ============================================== #
class Query(ObjectType):
"""
Queries for all apps in Opus
"""
# App: Config Generator
config_generator = Field(
ConfigGeneratorQuery,
name="config_generator",
description="Models for configuration generators"
)
# ============================================== #
# SCHEMA #
# ============================================== #
schema = Schema(query=Query)
This is then exposed to the browser via this config in django_project/urls.py
from django.urls import path
from graphene_django.views import GraphQLView
from django_project.graphql.schema import schema
# ============================================== #
# BASE ROUTES #
# ============================================== #
urlpatterns = [
...
# GraphQL Routes
path('graphql/', GraphQLView.as_view(graphiql=True, schema=schema)),
...
]
Finally, your app’s schemas should look something like this:
from graphene import ObjectType, List
# Custom Scripts
from app_config_generator.models import (
SwitchDaylightSavingsModel,
GPNSVlan
)
from app_config_generator.graphql.fields import (
GPNSVlanType,
SwitchDaylightSavingsType
)
# ============================================== #
# SUBQUERIES #
# ============================================== #
class SwitchDaylightSavings(ObjectType):
switch_daylight_savings = List(
SwitchDaylightSavingsType,
description="Pairs all of the available vendors by their daylight savings regions"
)
def resolve_switch_daylight_savings(self, info, **kwargs):
return SwitchDaylightSavingsModel.objects.all()
class GPNSVlans(ObjectType):
gpns_vlans = List(
GPNSVlanType,
description="All VLANs used by the GPNS standard to configure both firewalls and switches."
)
def resolve_gpns_vlans(self, info, **kwargs):
return GPNSVlan.objects.all()
# ============================================== #
# MAIN QUERY #
# ============================================== #
class ConfigGeneratorQuery(
SwitchDaylightSavings,
GPNSVlans,
):
"""
Groups all subqueries for Configuration Generators
"""
pass
With this I was able to call the "SwitchDaylightSavings" mutation, under the "config_generator" route. I don’t remember the details correctly, but this should give anyone a good head start. I think there are better and more elegant solutions for this, I especially recommend checking out Strawberry
It feels like you are looking for a way to federate your schema. This is something that's often done in javascript based GraphQL servers such as Apollo. Not sure there's a pure Graphene-based approach.
Feb 25 at 23:00
Man, so is there not any way to simulate it? Or use a special arrangement to get a similar behavior?
Feb 26 at 1:15
Gonna answer my own question. Apparently there is a module for graphene called
graphene-federation
which should solve my problems, but will hold making an official answer until I actually implement itFeb 26 at 22:41
@Eddysanoli interested to know your use-case on this. Ideally, Django apps are just a way to encapsulate your code depending on business logic, but I'm not able to understand how having it in your GraphQL query will help.
Feb 27 at 19:21
Hey! @SanyamKhurana, its mainly an organization thing. I have something like 50 models that I want to make available through the GraphQL API, but I dont really like that in the docs that get auto-generated just appear as a giant list. By grouping them the structure will make more sense for future developers (since it follows the same structure for each app) and will be more readable in the docs
Feb 27 at 20:30