Extending the GraphQL Schema
Extend WPGraphQL when frontend data needs custom schema fields.
Most projects only need fields that come from WordPress, ACF, registered post types, and blocks. When the frontend needs a value that does not naturally exist in the schema, extend WPGraphQL in the WordPress theme or plugin code.
Use the GraphiQL IDE while writing schema extensions. It is the quickest way to check the type names you are extending, the fields you have registered, and the shape returned by your resolver.
Add A Field
Use register_graphql_field when you need to add a computed field to an
existing schema type.
add_action('graphql_register_types', function () {
register_graphql_field('Film', 'totalSessions', [
'type' => 'Int',
'description' => 'The number of sessions for this film.',
'resolve' => function ($root) {
$sessions = get_post_meta($root->ID, 'total_sessions', true);
return $sessions ? (int)$sessions : null;
},
]);
});The resolver receives the current GraphQL root object first. For post types this
usually gives you access to $root->ID, which can be used with get_post_meta,
ACF, or project model helpers.
Return Posts
When a resolver returns a WordPress post and you want GraphQL subfields to keep
working, wrap the post in WPGraphQL\Model\Post.
add_action('graphql_register_types', function () {
register_graphql_field('RootQuery', 'currentProgram', [
'type' => 'Program',
'resolve' => function () {
$program = get_field('current_program', 'option');
if ($program) {
return new WPGraphQL\Model\Post($program);
}
return null;
},
]);
});The same applies to lists of posts:
register_graphql_field('Film', 'guests', [
'type' => ['list_of' => 'Guest'],
'resolve' => function ($root) {
$guests = Film::fromPost($root->ID)->getGuests();
return array_map(function ($id) {
return new WPGraphQL\Model\Post(get_post($id));
}, $guests);
},
]);Add A Root Query
Root fields are useful when the frontend needs a named project-level entry point, such as the current programme, search results, or a custom data system.
add_action('graphql_register_types', function () {
register_graphql_field('RootQuery', 'siteSearch', [
'type' => ['list_of' => 'ContentNode'],
'args' => [
'search' => ['type' => 'String'],
],
'resolve' => function ($root, $args) {
$query = new WP_Query([
's' => $args['search'] ?? '',
'post_type' => ['page', 'post'],
]);
return array_map(function ($post) {
return new WPGraphQL\Model\Post($post);
}, $query->posts);
},
]);
});Keep root fields narrow and intentional. If a field starts to behave like a feature API, consider whether it should be a query hook, a serverless function, or a dedicated WordPress endpoint instead.
Add Object Types
Use register_graphql_object_type when the field returns structured data that
does not already have a GraphQL type.
class FilmAssetProgram {
public function __construct(public WP_Post $program) {}
static function registerType() {
register_graphql_object_type('FilmAssetProgram', [
'fields' => [
'program' => [
'type' => 'Program',
'resolve' => function (FilmAssetProgram $program) {
return new WPGraphQL\Model\Post($program->program);
},
],
'buckets' => [
'type' => ['list_of' => 'FilmAssetBucket'],
'resolve' => function (FilmAssetProgram $program) {
return $program->getBuckets();
},
],
],
]);
}
}Then return that object type from a field:
add_action('graphql_register_types', function () {
FilmAssetProgram::registerType();
FilmAssetBucket::registerType();
register_graphql_field('RootQuery', 'assetBuckets', [
'type' => 'FilmAssetProgram',
'args' => [
'programId' => ['type' => 'ID'],
],
'resolve' => function ($root, $args) {
$program = get_post($args['programId']);
if (!$program) {
throw new \GraphQL\Error\UserError('Program not found');
}
return new FilmAssetProgram($program);
},
]);
});Add Enums
Enums are useful when a field has a small, known set of values.
enum EventType: string {
case Film = 'Film';
case Event = 'Event';
case Short = 'Short';
case Subscription = 'Subscription';
}
add_action('graphql_register_types', function () {
register_graphql_enum_type('EventType', [
'description' => 'Standard event types',
'values' => php_enum_cases_to_graphql_cases(EventType::cases()),
]);
});Enums make the generated TypeScript types more useful than a loose string.
Keep Extensions Small
Schema extensions are easiest to maintain when they sit close to the model they describe. Prefer small fields on the relevant type over one large root field that returns unrelated data.
When a resolver is expensive, cache the underlying data in WordPress or expose a
runtime query with an explicit # ttl comment in the .graphql file.