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
}
}
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