Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Table of Contents
stylenone

What is GraphQL?

GraphQL is a way to get data from an API where you can ask for exactly what you need and nothing more. Totara uses GraphQL for i

Concepts

For a detailed explanation of GraphQL concepts, please see our /wiki/spaces/~simonc/pages/119473875 documentation. However, if you just want to see the GraphQL API in action, please head straight to the Create an internal query section.

...

  1. Create a webapi directory in your plugin directory. For example, for my local_todo plugin, this would be in /server/local/todo/.

  2. Under the webapi directory, create an ajax directory.

  3. Create a schema.graphqls file under the ajax directory.

  4. In schema.graphqls, let’s define the todo_item type by adding the following:

    Code Block
    languagegraphql
    type local_todo_item {
        id: core_id
        title: String
        completed_at: core_date
    }

    This adds a new type with id, title and completed_at, which we will have access to when using this type.objectNo

Create the query

We’ll start off our APIs by creating a query to retrieve a list of the type todo_item.

  • In schema.graphqls define the result we’ll get back from the query by defining the following type:

    code

    Code Block
    languagegraphql
    type local_todo_items_result {
        items: [local_todo_item!]!
    }

    Notice here we’ve used ! in two places. This indicates the field is non-nullable. We’ve used this here to say that we always expect an array, by putting the exclamation mark on the outside of the array [...]!. We also used it to to say that the elements of the array cannot be null [my_type!].

  • Now we have the result defined, let’s define the query. We do this by extending the type Query and adding our query into it. Let’s do this:

    Code Block
    languagegraphql
    extend type Query {
        local_todo_items: local_todo_items_result!
    }

    This defines that the query local_todo_items should return the local_todo_items_result type we defined earlier.

Creating the persisted query

The persisted query is a definition of the query for the frontend front-end component to import. We name this file the query name, minus the component name of the plugin we’re working with.

...

  • Create the persisted query file in the webapi/ajax/items.graphql with the following:

    Code Block
    languagegraphql
    query local_todo_items {
        local_todo_items {
            items {
                id
                title
                completed_at
            }
        }
    }

Create the backend

Now we need to create the PHP backend to handle the query.

  • First we’ll create the directory for the query resolver by creating the following directory: /server/local/todo/classes/webapi/resolver/query

  • Now let’s create the query resolver class, items.php in the directory we just created.:

    Code Block
    languagephp
    <?php
    
    namespace local_todo\webapi\resolver\query;
    
    use core\webapi\execution_context;
    use core\webapi\query_resolver;
    use local_todo\entity\item;
    
    class items extends query_resolver {
    
        /**
         * @inheritDoc
         * @throws \coding_exception
         */
        public static function resolve(array $args, execution_context $ec) {
            global $USER;
    
            $items = item::repository()
                ->where('user_id', $USER->id)
                ->order_by('id')
                ->get();
    
            return ['items' => $items];
        }
    }

    In this file we’ve defined the query resolve and we’re using the ORM of local_todo to return a list of items.

  • Now we create the type resolver directory /server/local/todo/classes/webapi/resolver/type/ and the type resolver for item item.php in the directory:

    Code Block
    languagephp
    <?php
    
    namespace local_todo\webapi\resolver\type;
    
    use core\webapi\execution_context;
    use core\webapi\type_resolver;
    
    class item extends type_resolver {
        /**
         * @param string $field - The field being requested 
         * @param $source - In the case, source will be our `item` entity class as it's what's returned from the query resolver
         * @param array $args
         * @param execution_context $ec
         * @return mixed|void
         */
        public static function resolve(string $field, $source, array $args, execution_context $ec) {
            return $source->$field;
        }
    }

Create an internal mutation (e.q. create, update, delete)

In this example we’ll create a mutation to update the title of a local_todo_item.

Create the schema

  1. Create a webapi directory in your plugin directory. For example, for my local_todo plugin, this would be in /server/local/todo/.

  2. Under the webapi directory, create an ajax directory.

  3. Create a schema.graphqls file under the ajax directory.

Create the item’s type

In schema.graphqls, let’s define the todo_item type by adding the following:

Code Block
languagegraphql
type local_todo_item {
    id: core_id
    title: String
    completed_at: core_date
}

This adds a new type with id, title and completed_at, which we will have access to when using this type.

Create the input types

Our mutation will take two input: one will be our item reference (which item we want to update) and the other will be the new item title.

  1. Let’s start out with the input object to refer to an item. In schema.graphqls, let’s define the local_todo_update_item_reference

Code Block
languagegraphql
input local_todo_item_reference {
    id: core_id!
}

Notice the new input keyword we used here. This is input object which defines what information we need from the client. In this case it’s just id. For more information, see the GraphQL documentation:https://graphql.org/learn/schema/#input-object-types.

  1. Now we’ll add the input object to retrieve the new title.

Code Block
input local_todo_update_item_input {
    title: String!
}

Create the result type

The result type will define what we’ll return from the mutation.

  1. In schema.graphqls define the result type:

Code Block
type local_todo_update_item_result {
    item: local_todo_item
}

Create the mutation

Now we’ll create the mutation to update the title. We’ll name this local_todo_update_item and extend the type Mutation. Similar to how we did this with Query.

  1. In schema.graphqls, add the mutation:

Code Block
extend type Mutation {
    local_todo_update_item(
        item_reference: local_todo_item_reference
        input: local_todo_update_item_input
    ): local_todo_update_item_result!
}

Create the backend

Examples are simplified and use the ORM entity rather than the model for simplicity. Best practice would be to use the ORM model.

Now we need to create the PHP backend to handle the query.

  • First we’ll create the directory for the query resolver by creating the following directory: /server/local/todo/classes/webapi/resolver/query

  • Now let’s create the query resolver class, items.php in the directory we just created:

    Code Block
    languagephp
    <?php
    
    namespace local_todo\webapi\resolver\mutation;
    
    use core\webapi\execution_context;
    use core\webapi\mutation_resolver;
    use local_todo\entity\item;
    
    class update_item extends mutation_resolver {
    
        public static function resolve(array $args, execution_context $ec) {
            $item_id = $args['item_reference']['id'] ?? null;
            $new_title = $args['input']['title'] ?? null;
    
            $item = new item($item_id);
            $item->title = $new_title;
    
            return ['item' => $item];
        }
    }

    In this file we’ve defined the mutation resolver and we’re using the ORM entity of local_todo to update and return the item.

  • Now we create the type resolver directory /server/local/todo/classes/webapi/resolver/type/ and the type resolver for item item.php in the directory:

    Code Block
    languagephp
    <?php
    
    namespace local_todo\webapi\resolver\type;
    
    use core\webapi\execution_context;
    use core\webapi\type_resolver;
    
    class item extends type_resolver {
        /**
         * @param string $field - The field being requested 
         * @param $source - In the case, source will be our `item` entity class as it's what's returned from the query resolver
         * @param array $args
         * @param execution_context $ec
         * @return mixed|void
         */
        public static function resolve(string $field, $source, array $args, execution_context $ec) {
            return $source->$field;
        }
    }

Run the Mutation

If you haven’t already, please follow the Testing queries and mutations section to set up your developer environment.

The mutation to run would be:

Code Block
languagegraphql
mutation local_todo_update_item {
    local_todo_update_item(
        item_reference: {id: 8}
        input: {title: "My new title"}
    ) {
        item: {
            id
            title
        }
    }
}

Using internal GraphQL in Tui components

Please see our guide to creating your first component in Tui: Quick-start guide: Creating your first component. This guide covers how to create your first Tui component and also how to link it up to an GraphQL query.

Testing queries and mutations

...