This project is not covered by Drupal’s security advisory policy.

This module adds a GraphQL schema extension and relevant resolvers to enable CRUD (Create, Read, Update, Delete) operations for Drupal Books. In particular, it allows creating book outlines and attaching book pages to other pages, which is currently unsupported via other methods, like JSON:API.

Features

The module adds 2 kinds of queries: books returns all stored books and book(id) returns a particular book given its node ID.
It also adds 3 mutations: createBook(data), updateBook(id, data), and deleteBook(id).

Additional Requirements

To use the module, you will need to install and enable Book, GraphQL, and GraphQL Book modules. All pre-requisites should be installed automatically if this module is installed using composer.

Note: GraphQL 4.5 dependencies have a minimum stability bug in the requirements, so if it throws an error about specifying minimum stability, you may need to manually install its dependency `typed_data` module first:

composer require 'drupal/typed_data:^1.0@beta'

Post-Installation

Configure a GraphQL server as follows:

  1. In the Drupal administrator dashboard, go to Configuration -> GraphQL -> Servers
  2. Click "Create server"
  3. Give it a name, e.g. "Book endpoint"
  4. Choose schema to be "Composable Book schema"
  5. Under Schema configuration > Enabled extensions, tick the "Composable Book extension" (due to a bug in the GraphQL module, sometimes this gets reset on the first server save, so after saving it, click edit and come back to check this is still ticked)
  6. For endpoint, add "/graphql" unless you already have another GraphQL server running and want to separate them
  7. Set the remaining settings for your use case (it's also fine to leave them as default) and click "Save"

See the GraphQL module documentation for general usage guidance: README - GraphQL

For details about the GraphQL schema made available by this project, see the files under graphql/ in the project source code.

Supporting documentation

Book link fields exposed by this module are documented in BookOutlineStorageInterface::loadMultiple

Example usage

All Python code is prefixed with:

graphql_endpoint = "https://your_drupal.site/graphql"
from requests import session
with session() as session:

List all books

GraphQL query Python request Example response
query GetAllBooks {
  books {
    id,
  	title,
  	body {
      value,
      summary
    },
    links {
      bid
    }
  }
}
res = session.post(graphql_endpoint, json={
  "query": """query GetAllBooks {
    books {
      id,
      title,
      body {
        value,
        summary
      },
      links {
        bid
      }
    }
  }""",
})
res.json()
{
  "data": {
    "books": [
      {
        "id": "118",
        "title": "Test Book from GraphQL",
        "body": {
          "value": "",
          "summary": null
        },
        "links": {
          "bid": "0"
        }
      }
    ]
  }
}

Create a new book page

GraphQL query Python request Example response
mutation CreateTestBook {
  createBook(data: { title: "My Course Name", body: { value: "<h2>Welcome to my course<h2><br><p>It will be amazing.</p>" } }) {
    book {
      id,
      title,
      body {
        value,
        summary
      },
      links {
        nid
      }
    }
  }
}
res = session.post(graphql_endpoint, json={
  "query": """mutation CreateCourseBookPage($data: BookInput!) {
    createBook(data: $data) {
      errors,
      book {
        id,
        title,
        body {
          value,
          summary,
          format
        }
      }
    }
  }""",
  "variables": {
    "data": {
      "title": "My Course Name",
      "body": {
        "value": "<h2>Welcome to my course<h2><br><p>It will be amazing.</p>",
      },
    }
  }
})
new_book_id = res.json()['data']['createBook']['book']['id']
res.json()
{
  "data": {
    "createBook": {
      "book": {
        "id": "1",
        "title": "My Course Name",
        "body": {
          "value": "<h2>Welcome to my course<h2><br><p>It will be amazing.</p>",
          "summary": null,
          "format": "full_html"
        },
        "links": {
          "nid": null
        }
      }
    }
  }
}

Make the new course page into a book (creating a new book outline)

GraphQL query Python request Example response
mutation CreateLinkedBook {
  createBook(data: {
    title: "My Course Name",
    links: {
      bid: 1,
      pid: 1,
      p1: 1
    }
  }) {
    errors,
    book {
      id,
      links {
        nid,
        bid
      }
    }
  }
}
res = session.post(graphql_endpoint, json={
  "query": """mutation AddCourseBookOutline($id: ID!, $data: BookInput!) {
    updateBook(id: $id, data: $data) {
      errors,
      book {
        id,
        links {
          nid,
          bid
        }
      }
    }
  }""",
  "variables": {
    "id": new_book_id,
    "data": {
      "title": "My Course Name",
      "links": {
        "bid": new_book_id
      }
    }
  }
})
res.json()
{
  "data": {
    "createBook": {
      "errors": [],
      "book": {
        "id": "1",
        "links": {
          "nid": "1",
          "bid": "1"
        }
      }
    }
  }
}

Create a child content page

GraphQL query Python request Example response
mutation CreateLinkedBook {
  createBook(data: {
    title: "Another book page from GraphQL",
    body: {
      value: "don't read me",
      summary: "dangerous",
    },
    links: {
      bid: 1,
      pid: 1,
      p1: 1,
    }
  }) {
    errors,
    book {
      id,
      links {
        nid,
        bid
      }
    }
  }
}
def create_content_page(title, summary, body, book_id, parent_id):
  res = session.post(graphql_endpoint, json={
    "query": """mutation CreateCourseContentPage($data: BookInput!) {
        createBook(data: $data) {
          errors,
          book {
            id,
            title,
            body {
              value,
              summary,
              format
            },
            links {
              bid,
              pid
            }
          }
        }
    }""",
    "variables": {
      "data": {
        "title": title,
        "body": {
          "summary": summary,
          "value": body,
        },
        "links": {
          "bid": book_id,
          "pid": parent_id,
        }
      }
    }
  })
  print(res.json())
  return res.json()['data']['createBook']['book']['id']
{
  "data": {
    "createBook": {
      "errors": [],
      "book": {
        "id": "2",
        "links": {
          "nid": "2",
          "bid": "1"
        }
      }
    }
  }
}

Project information

Releases