GraphQL API deprecation process

This page contains technical information about how we deprecate code. For details on our deprecation policies see Changes to the GraphQL APIs.

Handling specific cases

Below is some additional detail on how to handle specific API changes.

Adding to schema

In general, it is safe to add new items to the schema at any point as a non-breaking change. Use the @since flag in PHPDocs, particularly if adding something in a minor release. This includes new persisted queries or mutations, new query or mutation endpoints/resolvers, new fields on existing endpoints, etc.

Adding optional arguments or properties to existing schema items

You can add new arguments and properties as a non-breaking change as long as they are optional. In the case of arguments, they must be added after existing arguments so any existing queries that don't contain the new arguments continue to work.

Renaming schema items

If you rename schema items this will be a breaking change - consider it the same as adding one item and removing another. Ensure that the old name continues to work by redirecting within the resolver. You should deprecate the old name and add appropriate debugging() calls and @deprecated references in the documentation.

Modifying the purpose of a schema item

We consider changes that alter the purpose of a specific query or mutation to be a breaking change, even if the structure of the schema remains unchanged. For example, if the 'current_learning' query was changed to include historic data. In situations such as this, our guidelines would recommend creating a new separate query and deprecating the old one if it is no longer required.

Deprecation process

'Deprecation' refers to the process of making changes to the schema in a way that will give sufficient warning before making breaking changes. 'Schema items' refers to anything that is defined in the current schema, including types, queries, mutations, and enums.

When we intend to make breaking changes to the API, we will signal an upcoming change in the next major version via deprecation. Deprecation is the process of marking a specific feature as no longer suitable for use.

In general, our GraphQL deprecation process is very similar to our general code Deprecation guidelines, but below we cover the process specifically for API changes.

1. Remove all references to the item from core code

When we deprecate functionality we remove all uses of the functionality from core code. Any core references to deprecated functionality should be treated as a bug.

2. Marking schema as deprecated

If the schema item is a field or enum, mark the GraphQL schema item as deprecated using the @deprecated directive. Provide a reason which explains why the field has been deprecated and points to any alternative:

type editor_weka_editor {
  repository_data: editor_weka_repository_data!
    @deprecated(
      reason: "The repository_data is now fetched from query editor_weka_repository_data"
    )
}

Note that if a field gets deprecated with the directive, a debugging message will automatically be thrown if the field is still used. In addition, deprecated functionality is displayed differently in GraphQL client documentation:

Note that other GraphQL schema objects such as queries and mutations don't support the deprecated directive.

3. Deprecate resolver class

Mark any resolver PHP code as deprecated using PHPDocs syntax and add a debugging() warning in case it is called. Provide a message to indicate how a developer should achieve the same result in the new code, and if possible call the new method directly so the resolver continues to work:

/**
 * @deprecated This type has been deprecated, please use 'new_type' instead.
 */
class deprecated_query extends query_resolver {
    public static function resolve(array $args, execution_context $ec) {
        // Throw a debugging warning with an appropriate message
        debugging(
            "The query 'deprecated_query' had been deprecated, please use 'new_query' query instead.",
            DEBUG_DEVELOPER
        );
        // Pass the request to the new query resolver.
        return new_query::resolve($args, $ec);
    }
}

This is particularly important for queries and mutation resolvers.

Here is an example of a single field that's been deprecated in an active type:

class existing_type extends type_resolver {
    public static function resolve(string $field, $source, array $args, execution_context $ec) {
        if ($field == 'deprecated_field') {
            // Throw a debugging warning with an appropriate message.
            debugging(
                "The field 'deprecated_field' had been deprecated. Please use the 'alternative_query' which returns the 'new_type' type instead.",
                DEBUG_DEVELOPER
            );
        }
        // Continue to resolve type as before.
    }
}

4. Mention in upgrade.txt

Describe the changes in upgrade.txt as per our normal deprecation guidelines.

5. Remove the deprecated code

After the next major release, deprecated code can be safely removed from the codebase. This should be done before the first point release of the new major version.