Create GraphQL schemas

Version: 22.x

Schemas describe the way that data is organized and constructed.

If your Sitecore instance contains data or business logic not exposed through the Sitecore-provided GraphQL schemas, you might want to create your own schema provider. You can use a schema provider to add self-contained pieces of a schema. 

If you want to add a new root query and your type system does not depend on any other schema provider’s types, you must select a schema provider. A good example of a schema provider is a third-party CRM system.

Implement a schema provider

To implement a schema provider:

  1. Create a C# class that implements the SchemaProviderBase class. This class defines the structure of the GraphQL schema, including the root fields that can be queried (an item is a root query field in the content schema provider). You create other supporting classes that define graph types (the node types in the schema).

  2. Register the schema provider on a GraphQL endpoint. This is a type registration in the endpoint’s config patch.

The following example shows a complete schema provider implementation. It allows querying on the current Sitecore user:

RequestResponse
using System;
using System.Collections.Generic;
using System.Web;
using GraphQL.Resolvers;
using GraphQL.Types;
using Sitecore.Security.Accounts;
using Sitecore.Services.GraphQL.Schemas;

namespace Sitecore.Services.GraphQL.Examples
{
    /// <summary>
    /// Sample of making your own schema provider
    /// This sample enables you to query on the current context user
    /// </summary>
    public class WhoAmISchemaProvider : SchemaProviderBase
    {
        public override IEnumerable<FieldType> CreateRootQueries()
        {
            yield return new WhoAmIQuery();
        }

        /// <summary>
        /// Teaches GraphQL how to resolve the `whoAmI` root field.
        ///
        /// RootFieldType<UserGraphType, User> means this root field maps a `User` domain object into the `UserGraphType` graph type object.
        /// </summary>
        protected class WhoAmIQuery : RootFieldType<UserGraphType, User>
        {
            public WhoAmIQuery() : base(name: "whoAmI", description: "Gets the current user")
            {
            }

            protected override User Resolve(ResolveFieldContext context)
            {
                // this is the object the resolver maps onto the graph type
                // (see UserGraphType below). This is your own domain object, not GraphQL-specific.
                return Context.User;
            }
        }

        // because this graph type is referred to by the return type in the FieldType above, it is automatically
        // registered with the schema. For implied types (e.g. interface implementations) you need to override CreateGraphTypes() and
        // manually tell the schema they exist (because no graph type directly refers to those types)
        protected class UserGraphType : ObjectGraphType<User>
        {
            public UserGraphType()
            {
                // graph type names must be unique within a schema, so if defining a multiple-schema-provider
                // endpoint, ensure that you don't have name collisions between schema providers.
                Name = "SitecorePrincipal";

                Field<NonNullGraphType<StringGraphType>>("name", resolve: context => context.Source.Name);
                Field<NonNullGraphType<StringGraphType>>("fullName", resolve: context => string.IsNullOrWhiteSpace(context.Source.Profile.FullName) ? context.Source.Name : context.Source.Profile.FullName);
                Field<NonNullGraphType<StringGraphType>>("icon", resolve: context => $"{HttpContext.Current?.Request.Url.GetLeftPart(UriPartial.Authority)}/-/icon/{context.Source.Profile.Portrait}");
                Field<NonNullGraphType<BooleanGraphType>>("isAuthenticated", resolve: context => context.Source.IsAuthenticated);
                Field<NonNullGraphType<BooleanGraphType>>("isAdministrator", resolve: context => context.Source.IsAdministrator);

                // note that graph types can resolve other graph types; for example
                // it would be possible to add a `lockedItems` field here that would
                // resolve to an `Item[]` and map it onto `ListGraphType<ItemInterfaceGraphType>`
            }
        }
    }
}
Note

This example uses nested classes because the schema is small. Real schemas are larger, and we recommend you split RootFieldTypes and GraphTypes into separate files.

To register the schema provider with an endpoint, use a Sitecore config patch file similar to this:

RequestResponse
<?xml version="1.0" encoding="utf-8" ?>

<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
    <sitecore>
        <api>
            <GraphQL>
                <endpoints>
                    <master type="Sitecore.Services.GraphQL.Hosting.GraphQLEndpoint, Sitecore.Services.GraphQL">
                        <schema hint="list:AddSchemaProvider">
                            <whoDat type="Sitecore.Services.GraphQL.Examples.WhoAmISchemaProvider, Sitecore.Services.GraphQL.Examples.NetFxHost" />
                        </schema>
                    </master>
                </endpoints>
            </GraphQL>
        </api>
    </sitecore>
</configuration>

Extend GraphQL schemas

You can modify or add to an existing schema by using an extender. Extenders are processed after schema providers, and they can modify or add types to the completed schema. This means that they can modify or add to the schema from more than one schema provider. Use an extender if you want to add a field to an existing type provided by a schema provider, hook an external API onto an existing type, or otherwise modify the schema provided by a schema provider.

A good example of when you use an extender is if you want to get API data from an item that contains a third-party API ID, such as a YouTube video ID: You can use the YouTube API to retrieve the description of the video and expose it via GraphQL alongside the Video item type.

To extend a GraphQL schema:

  • Create a schema extender with a class that extends the SchemaExtender class and a registration with the GraphQL endpoint in configuration, similar to a schema provider:

    RequestResponse
    using GraphQL.Resolvers;
    using GraphQL.Types;
    using Sitecore.Data.Fields;
    using Sitecore.Services.GraphQL.Schemas;
    using FieldType = GraphQL.Types.FieldType;
    
    namespace Sitecore.Services.GraphQL.Examples
    {
        /// <summary>
        /// Demonstrates some of the power of using schema extenders
        /// </summary>
        public class SimpleExtender : SchemaExtender
        {
            /// <summary>
            /// This is a simple example of the capabilities of an extender. It's designed to show the right way to do some common needs.
            /// </summary>
            public SimpleExtender()
            {
                // Extend the 'Appearance' graph type
                ExtendType("Appearance", type =>
                {
                    type.Description = "Modified by extender!";
                });
    
                // Extend the 'Appearance' graph type, assuming that it is also a derivative of IComplexGraphType
                // useful because IComplexGraphType is the first type that brings Fields into the type (e.g. not a scalar)
                ExtendType<IComplexGraphType>("Appearance", type =>
                {
                    // Extend every string field on the type and hack its description
                    ExtendField<StringGraphType>(type, field =>
                    {
                        field.Description = "I got hacked by an extender!";
                    });
    
                    // Extend a field by name and tweak its description
                    ExtendField(type, "contextMenu", field =>
                    {
                        field.Description = "Yoink! Gotcher description!";
                    });
                });
    
                // extends any type which defines a mapping for the Field backend type
                // (e.g. all things that represent template fields)
                ExtendTypes<ObjectGraphType<Field>>(type =>
                {
                    // add a new field to the field object type
                    // note the resolve method's Source property is the Field so you can get at its data
                    type.Field<StringGraphType>("bar",
                        description: "Field added to all fields by an extender",
                        resolve: context => "I'm adding this string to the display name: " + context.Source.DisplayName);
                });
    
                // Extends three named types and adds a 'foo' field to them
                ExtendTypes<IComplexGraphType>(new[] { "ItemLanguage", "ItemWorkflow", "ItemWorkflowState" }, type =>
                {
                    // add a "foo" field that returns "foo, bar, bas" to every complex type in the schema
                    // note: using a more specific generic than IComplexGraphType (e.g. ObjectGraphType<T>) may provide
                    // superior options when adding fields like the Field<T> method
                    type.AddField(new FieldType
                    {
                        Name = "foo",
                        Description = "A field passed in from an extender",
                        Resolver = new FuncFieldResolver<string>(context => "foo, bar, bas"),
                        Type = typeof(StringGraphType)
                    });
                });
    
                ExtendTypes(type =>
                {
                    // this will be called for _every_ type in the whole schema
                });
    
                // You can also add graph types, for example, to add complex data as a new field.
                // This type is added, as opposed to being used. It will appear in the schema
                // but cannot be queried because it's not attached to any other node in the graph
                // (for example, as a root query or as a property on another graph type)
                AddType(() => new FooGraphType());
            }
    
            protected class FooGraphType : InterfaceGraphType
            {
                public FooGraphType()
                {
                    Name = "Foo";
                    Field<StringGraphType>("bar");
                }
            }
        }
    }
    
  • The following example shows registering an extender on a GraphQL endpoint:

    RequestResponse
    <?xml version="1.0" encoding="utf-8" ?>
    
    <configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
        <sitecore>
            <api>
                <GraphQL>
                    <endpoints>
                        <master>
                            <extenders hint="list:AddExtender">
                                <simpleExtender type="Sitecore.Services.GraphQL.Examples.SimpleExtender, Sitecore.Services.GraphQL.Examples" />
                            </extenders>
                        </master>
                    </endpoints>
                </GraphQL>
            </api>
        </sitecore>
    </configuration>
    

Do you have some feedback for us?

If you have suggestions for improving this article,