Wagtail Grapple

repository·main·Indexed 41 days ago

https://github.com/torchbox/wagtail-grapple

A library that simplifies the creation of GraphQL endpoints for Wagtail data. It allows developers to expose Wagtail models—including Pages, Snippets, and Images—to GraphQL by annotating them with a `graphql_fields` list, abstracting the complexity of Graphene and Graphene Django. It includes support for paginated queries, StreamField blocks, and custom schema hooks for queries, mutations, and subscriptions.

Tokens
13.3K
Snippets
42
Records
52
Agent score
97%

What's inside Wagtail Grapple

  1. Overview of Wagtail Grapple

    main

    Wagtail Grapple is a tool designed to build GraphQL endpoints for Wagtail models on a model-by-model basis with minimal configuration. Instead of writing complex serializers, you can expose models to GraphQL by adding a graphql_fields list directly to your Django models.

    Key features include:

    • Automatic Type Creation: Create GraphQL types via model annotations.
    • Wagtail Model Support: Works with Pages (including StreamField and Orderable), Snippets, Images, Documents, Media, Settings, Redirects, and Search.
    • Advanced Capabilities: Supports custom Image & Document models, pagination, and headless preview functionality using GraphQL Subscriptions.
  2. How middleware works in Grapple

    main

    Middleware in Grapple allows you to intercept and affect the evaluation of fields within your schema.

    A middleware is any object or function that implements a resolve(next_middleware, *args) method. This follows the standard Graphene middleware pattern where the middleware can decide whether to call the next function in the chain or return a value early (e.g., to restrict access).

  3. Create GraphQL types by annotating models

    main

    To expose a Wagtail model to GraphQL using Grapple, add a graphql_fields attribute to your model class. This list defines which fields should be included in the generated GraphQL type. This approach avoids the need for manual serializer definitions.

    class MyModel(Page):
        # Example of the pattern described
        graphql_fields = ['title', 'body', 'some_other_field']
  4. What are Grapple Model Types?

    main

    In Grapple, a type is a descriptor function that instructs Grapple how a Django model field should be represented in your GraphQL schema.

    Grapple provides several built-in types for common Django fields (like GraphQLString, GraphQLInt, etc.), but you can also define custom types using Graphene by leveraging the generic GraphQLField type.

  5. Add custom GraphQL interfaces to models or blocks

    main

    You can attach additional GraphQL interfaces to Wagtail Pages, Django models, or StreamField blocks by defining the graphql_interfaces attribute. This attribute accepts a list or a tuple of interface classes. These interfaces will be appended to the base interfaces already provided by Grapple.

    Implementation Examples

    For a Wagtail Page model:

    from wagtail.models import Page
    from .interfaces import MyInterface
    
    class MyPage(Page):
        graphql_interfaces = (MyInterface,)

    For a standard Django model:

    from django.db import models
    from .interfaces import MyInterface
    
    class MyModel(models.Model):
        graphql_interfaces = (MyInterface,)

    For a StreamField block:

    from wagtail.core import blocks
    from .interfaces import MyInterface
    
    class MyStructBlock(blocks.StructBlock):
        graphql_interfaces = (MyInterface,)
    # Example interface definition
    class MyInterface(graphene.Interface):
        custom_field = graphene.String()
    
    # Adding to a Page model
    class MyPage(Page):
        graphql_interfaces = (MyInterface,)
  6. Enable headless previews with Wagtail Headless Preview

    main

    Grapple supports real-time headless previews by integrating with the wagtail-headless-preview package. This allows you to pass a unique 'preview token' to your GraphQL endpoint, enabling you to preview page changes in real-time as they are being edited in the Wagtail admin.

    To implement this, you must follow the setup instructions provided by the wagtail-headless-preview package documentation.

  7. Define GraphQL models using graphql_fields

    main

    Grapple automatically builds GraphQL types by iterating over your project's models. To expose specific fields via GraphQL, add a graphql_fields attribute to your Wagtail model. This attribute should be a list of Grapple-specific field types (like GraphQLString, GraphQLRichText, or GraphQLStreamfield) that map to your model's existing fields.

    When you define graphql_fields, the structure of the resulting GraphQL type is determined by the list you provide. For example, a StreamField in Wagtail can be exposed using GraphQLStreamfield to allow querying its block structure and raw values in GraphQL.

    from django.db import models
    from grapple.models import GraphQLRichText, GraphQLStreamfield, GraphQLString
    from wagtail import blocks
    from wagtail.models import Page
    from wagtail.images.blocks import ImageBlock
    from wagtail.fields import RichTextField, StreamField
    from wagtail.admin.edit_handlers import FieldPanel
    
    class BlogPage(Page):
        author = models.CharField(max_length=255)
        date = models.DateField("Post date")
        summary = RichTextField()
        body = StreamField(
            [
                ("heading", blocks.CharBlock(classname="full title")),
                ("paragraph", blocks.RichTextBlock()),
                ("image", ImageBlock()),
            ]
        )
    
        content_panels = Page.content_panels + [
            FieldPanel("author"),
            FieldPanel("date"),
            FieldPanel("summary"),
            FieldPanel("body"),
        ]
    
        # Define which fields are exposed to GraphQL
        graphql_fields = [
            GraphQLString("heading"),
            GraphQLString("date"),
            GraphQLString("author"),
            GraphQLRichText("summary"),
            GraphQLStreamfield("body"),
        ]
  8. Configure wagtail-grapple in Django settings

    main

    To enable Grapple, you must perform three configuration steps in your Django settings and URL configuration:

    1. Add to INSTALLED_APPS: Include "grapple" and "graphene_django" in your INSTALLED_APPS list.
    2. Configure Grapple and Graphene:
      • Set GRAPHENE = {"SCHEMA": "grapple.schema.schema"} to point to the Grapple schema.
      • Set GRAPPLE = {"APPS": ["app_name"]} where "app_name" is the list of Django apps you want Grapple to scan for models to generate GraphQL types.
    3. Register GraphQL URLs: Include grapple_urls in your urlpatterns in urls.py.
    # settings.py
    INSTALLED_APPS = [
        # ...
        "grapple",
        "graphene_django",
        # ...
    ]
    
    # Grapple Config:
    GRAPHENE = {"SCHEMA": "grapple.schema.schema"}
    GRAPPLE = {
        "APPS": ["home"],
    }
    
    # urls.py
    from django.urls import path, include
    from grapple import urls as grapple_urls
    
    urlpatterns = [
        # ...
        path("", include(grapple_urls)),
        # ...
    ]
  9. Enable GrappleMiddleware for field-level middleware

    main

    To ensure that Grapple's field-level middleware functions correctly, you must enable grapple.middleware.GrappleMiddleware in your Graphene settings. This is done by adding the class path to the MIDDLEWARE list within the GRAPHENE configuration dictionary in your settings.py file.

    # settings.py
    GRAPHENE = {
        # ...
        "MIDDLEWARE": ["grapple.middleware.GrappleMiddleware"],
    }
  10. Expose the GraphQL endpoint in URLs

    main

    To make the GraphQL API accessible, include the grapple_urls in your project's urls.py file. This typically mounts the endpoint at /api/graphql/ if you use the api/ prefix.

    from django.urls import include, path
    from grapple import urls as grapple_urls
    
    # ...
    urlpatterns = [
        # ...
        path("api/", include(grapple_urls)),
        # ...
    ]
  11. Configure Wagtail Grapple settings

    main

    After installation, you must update your Django settings to include the necessary apps and configuration dictionaries.

    1. Add grapple and graphene_django to your INSTALLED_APPS.
    2. Define the GRAPHENE dictionary to point to your schema.
    3. Define the GRAPPLE dictionary with a list of apps to scan for GraphQL models.
    INSTALLED_APPS = [
        # ...
        "grapple",
        "graphene_django",
        # ...
    ]
    
    # Grapple config:
    GRAPHENE = {"SCHEMA": "grapple.schema.schema"}
    GRAPPLE = {
        "APPS": ["home"],
    }