Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 20 Next »

In this guide we’ll look at a plugin named local_todo (which is the plugin todo in the local directory) as an example.

What is GraphQL?

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.

Schema types

In Totara, schemas are categorised based on their use cases. Each schema type - queries, mutations, types, etc. - follows specific rules about its availability and interactions with other schema types. Totara defines three primary schema types: external, internal, and mobile.

External schema

The external schema is used for defining APIs accessible to external systems. It serves as the ‘root’ schema since it can be accessed by other schemas.

External schemas are stored in the plugin's webapi/ directory. For example, in the local_todo plugin, the external schema file would be located at:

server/local/todo/webapi/schema.graphqls

Internal schema

The internal schema, also referred to as ajax in the codebase, is designed for internal client operations. It is isolated and not accessible to other schema types.

Internal schemas are stored in the plugin's webapi/ajax/ directory. For example, in the local_todo plugin, the internal schema file would be located at:

server/local/todo/webapi/ajax/schema.graphqls

For more information, refer to Totara's developer documentation or check the guide below on creating an internal query.

Mobile schema

The mobile schema is tailored for APIs used by the Totara Mobile app.

Mobile schemas are stored in the plugin's webapi/mobile/ directory. For example, in the local_todo plugin, the mobile schema file would be located at:

server/local/todo/webapi/mobile/schema.graphqls

Create an internal query

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.

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

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

    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:

    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 front-end component to import. We name this file the query name, minus the component name of the plugin we’re working with.

Persisted queries use .graphql instead of the .graphqls we used previously for the schema file.

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

    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:

    <?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:

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

Testing queries and mutations

To test your queries and mutations, please see our guide to using the developer GraphQL API: Making developer requests via a GraphQL client

  • No labels