WP Manifestindependent plugin directory
manifest / developer / pk-graphql-kit

PK GraphQL Kit

Developer toolkit for headless WordPress — CPT, ACF, WPGraphQL integrations and schema in one place

by Petro Krupenia · github.com/petrokrupenia/pk-graphql-kit · website

0stars
0forks

Install

No release zip yet. The repository archive installs, but the folder name will carry the branch suffix and updates will not flow:

wp plugin install https://github.com/petrokrupenia/pk-graphql-kit/archive/refs/heads/master.zip

Developer toolkit for headless WordPress — CPT, ACF, WPGraphQL schema in one place.

Requirements

Installation

  1. Clone or copy the plugin into wp-content/plugins/pk-graphql-kit/
  2. Run composer install
  3. Activate the plugin in WordPress admin

Overview

The plugin is configured by editing files inside includes/. There is no settings UI for configuration — every manager exposes protected methods you override directly.

The admin overview page is available at Settings → PK GraphQL Kit.


Post Types & Taxonomies

Edit includes/PostType/PostTypeManager.php.

Register post types

protected function post_types(): array {
    return [
        [ 'slug' => 'book', 'singular' => 'Book', 'plural' => 'Books' ],
        [ 'slug' => 'team-member', 'singular' => 'Team Member', 'plural' => 'Team Members' ],
    ];
}

All post types are registered with show_in_graphql, show_in_rest, and auto-generated graphql_single_name / graphql_plural_name by default. Pass args to override any defaults.

Register taxonomies

protected function taxonomies(): array {
    return [
        [ 'slug' => 'genre', 'singular' => 'Genre', 'plural' => 'Genres', 'post_types' => [ 'book' ] ],
    ];
}

Taxonomies default to hierarchical => true and full GraphQL support.

Unregister

protected function unregister_post_types(): array {
    return [ 'some-post-type' ];
}

protected function unregister_taxonomies(): array {
    return [ 'some-taxonomy' ];
}

Enable WPGraphQL on external post types

For post types registered by other plugins that don't have GraphQL support:

protected function graphql_post_types(): array {
    return [ 'page', 'product' ];
}

protected function graphql_taxonomies(): array {
    return [ 'category' ];
}

graphql_single_name and graphql_plural_name are auto-generated from the post type labels.

Helpers

use PkGraphQLKit\PostType\PostTypeHelper;
use PkGraphQLKit\PostType\TaxonomyHelper;

// Custom post types only (no built-ins)
PostTypeHelper::all();
PostTypeHelper::public_types();
PostTypeHelper::non_public_types();
PostTypeHelper::graphql_types();

// All post types including built-ins (useful in integrations)
PostTypeHelper::all_graphql_types();

// Enable GraphQL support manually
PostTypeHelper::enable_graphql( [ 'page', 'product' ] );

Same methods available on TaxonomyHelper.

Filters

Filter Description
pkgraphqlkit/register/post_types Modify post types array before registration
pkgraphqlkit/register/taxonomies Modify taxonomies array before registration
pkgraphqlkit/unregister/post_types Modify slugs array before unregistering
pkgraphqlkit/unregister/taxonomies Modify slugs array before unregistering
pkgraphqlkit/graphql/enable/post_types Modify slugs array before enabling GraphQL
pkgraphqlkit/graphql/enable/taxonomies Modify slugs array before enabling GraphQL

ACF

Edit includes/Acf/AcfManager.php. Only available when ACF is active.

Options pages

protected function options_pages(): array {
    return [
        'Site Settings',
        [ 'page_title' => 'SEO Settings', 'parent_slug' => 'options' ],
    ];
}

ACF JSON

Field groups created in the ACF admin are automatically saved to and loaded from acf-json/ in the plugin root. Commit this directory to version control.

Dynamic fields

To dynamically populate field choices (e.g. a select from an options page), create a class in includes/Acf/DynamicFields/ that extends AcfDynamicFields:

namespace PkGraphQLKit\Acf\DynamicFields;

use PkGraphQLKit\Acf\AcfDynamicFields;

class MyDynamicFields extends AcfDynamicFields {
    protected function register_filters(): void {
        add_filter( 'acf/load_field/name=my_select', [ $this, 'load_my_select' ] );
    }

    public function load_my_select( array $field ): array {
        $field['choices'] = [ 'foo' => 'Foo', 'bar' => 'Bar' ];
        return $field;
    }
}

The class is discovered and booted automatically — no registration required.

Code-based field groups

Create a class in includes/Acf/FieldGroups/ that implements AcfFieldGroupInterface — it is discovered and registered automatically. Use /make-acf-field-group to generate the skeleton, or register manually via AcfManager::register_field_group().

Filters

Filter Description
pkgraphqlkit/acf/options_pages Add or modify options pages before registration

WPGraphQL

Settings

Edit includes/GraphQL/GraphQLSettings.php.

protected function settings(): array {
    return [
        'graphql_endpoint'             => 'graphql',
        'public_introspection_enabled' => 'on',
    ];
}

Boolean settings use 'on' / 'off' strings as WPGraphQL expects. Settings defined here override the WPGraphQL admin UI.

Environment presetsdebug_mode_enabled and tracing_enabled are automatically set to 'on' for local, development, and staging environments. Values in settings() always take precedence.

CORS

protected function allowed_origins(): array {
    return [
        get_bloginfo( 'url' ),  // always included
        'https://yoursite.com',
        'http://localhost:3000',
    ];
}

Custom types, fields, mutations

Edit includes/GraphQL/GraphQLManager.php.

protected function types(): array {
    return [
        [
            'type_name' => 'Address',
            'config'    => [
                'description' => 'A physical address.',
                'fields'      => [
                    'street' => [ 'type' => 'String' ],
                    'city'   => [ 'type' => 'String' ],
                ],
            ],
        ],
    ];
}

protected function fields(): array {
    return [
        [
            'type_name'  => 'Post',
            'field_name' => 'readingTime',
            'config'     => [
                'type'    => 'Int',
                'resolve' => fn( $post ) => (int) get_post_meta( $post->databaseId, '_reading_time', true ),
            ],
        ],
    ];
}

protected function mutations(): array {
    return [
        [
            'name'   => 'SubmitContactForm',
            'config' => [
                'inputFields'         => [
                    'name'    => [ 'type' => [ 'non_null' => 'String' ] ],
                    'email'   => [ 'type' => [ 'non_null' => 'String' ] ],
                    'message' => [ 'type' => [ 'non_null' => 'String' ] ],
                ],
                'outputFields'        => [
                    'success' => [ 'type' => 'Boolean' ],
                    'message' => [ 'type' => 'String' ],
                ],
                'mutateAndGetPayload' => function ( array $input ): array {
                    // handle the mutation
                    return [ 'success' => true, 'message' => 'Sent!' ];
                },
            ],
        ],
    ];
}

Filters

Filter Description
pkgraphqlkit/graphql/register/types Modify types array before registration
pkgraphqlkit/graphql/register/fields Modify fields array before registration
pkgraphqlkit/graphql/register/mutations Modify mutations array before registration

Integrations

Integrations add WPGraphQL support for third-party plugins. They are auto-discovered — create a file in includes/Integration/Integrations/, extend AbstractIntegration, and it will be loaded automatically when the target plugin is active.

namespace PkGraphQLKit\Integration\Integrations;

use PkGraphQLKit\Integration\AbstractIntegration;

class MyPluginIntegration extends AbstractIntegration {

    public function get_plugin_file(): string {
        return 'my-plugin/my-plugin.php';
    }

    protected function graphql_fields(): array {
        return [
            [
                'type_name'  => 'Post',
                'field_name' => 'myField',
                'config'     => [
                    'type'    => 'String',
                    'resolve' => fn( $post ) => get_post_meta( $post->databaseId, '_my_field', true ),
                ],
            ],
        ];
    }
}

Override register() and call parent::register() if additional hooks beyond GraphQL schema registration are needed.

Bundled integrations

Integration Plugin
SlimSeoIntegration Slim SEO — exposes seo { title, description, noindex, canonical, og { title, description, image }, twitter { card, image, site } } on all GraphQL-enabled post types and taxonomies

Accessing managers programmatically

pkgraphqlkit()->post_types()       // PostTypeManager
pkgraphqlkit()->acf()              // AcfManager|null
pkgraphqlkit()->integrations()     // IntegrationManager
pkgraphqlkit()->graphql()          // GraphQLManager
pkgraphqlkit()->graphql_settings() // GraphQLSettings
pkgraphqlkit()->admin()            // AdminPage

Claude Code Skills

Skills are slash commands for Claude Code that generate or modify plugin code directly. They live in .claude/commands/ inside the plugin and are available automatically when Claude Code is opened from the plugin directory.

Available skills

Skill Arguments Example
/register-cpt slug singular plural /register-cpt book Book Books
/register-taxonomy slug singular plural [post_type1,post_type2] /register-taxonomy genre Genre Genres book
/unregister cpt\|taxonomy slug /unregister cpt attachment
/add-cors url [url2 ...] /add-cors https://mysite.com http://localhost:3000
/toggle-integrations on\|off /toggle-integrations off
/add-graphql-field TypeName fieldName ReturnType [description] /add-graphql-field Post readingTime Int "Reading time"
/add-mutation MutationName [description] /add-mutation SubmitContactForm
/add-acf-options-page Title [parent:parent-slug] /add-acf-options-page "Site Settings"
/make-acf-field-group ClassName "Title" [location] /make-acf-field-group BookFields "Book Fields" post-type:book
/make-acf-dynamic-fields ClassName [field1,field2] /make-acf-dynamic-fields ProductChoices product_type

/make-acf-field-group location options: post-type:slug, options, page, user.

Working from a higher-level directory

Skills are only loaded by Claude Code from the working directory it is opened in. If you open Claude Code from a parent directory (e.g. the WordPress root at app/public), create symlinks so the skills are available there too:

# Run once from the WordPress root (app/public)
mkdir -p .claude/commands
for f in wp-content/plugins/pk-graphql-kit/.claude/commands/*.md; do
    ln -sf "../../$f" ".claude/commands/$(basename $f)"
done

The symlinks at app/public/.claude/commands/ are already committed to this repository. If you add new skills to the plugin, re-run the command above to pick them up.


Directory structure

.claude/
└── commands/                        — Claude Code skills (slash commands)
includes/
├── Admin/
│   └── AdminPage.php               — admin overview page (Settings → PK GraphQL Kit)
├── Acf/
│   ├── AcfDynamicFields.php        — base class for dynamic field population
│   ├── AcfFieldGroupInterface.php  — interface for code-based field groups
│   ├── AcfManager.php              — options pages, field groups, ACF JSON path
│   ├── DynamicFields/              — auto-discovered dynamic field classes
│   └── FieldGroups/                — auto-discovered code-based field group classes
├── GraphQL/
│   ├── GraphQLManager.php          — custom types, fields, mutations
│   └── GraphQLSettings.php         — WPGraphQL settings overrides + CORS
├── Helpers/
│   └── StringHelper.php            — to_graphql_name() utility
├── Integration/
│   ├── AbstractIntegration.php     — base class for integrations
│   ├── IntegrationManager.php      — auto-discovers and boots integrations
│   └── Integrations/               — auto-discovered integration classes
└── PostType/
    ├── PostTypeConfig.php           — value object for CPT registration
    ├── PostTypeHelper.php           — static helpers for querying post types
    ├── PostTypeManager.php          — registers/unregisters CPTs and taxonomies
    ├── TaxonomyConfig.php           — value object for taxonomy registration
    └── TaxonomyHelper.php           — static helpers for querying taxonomies
acf-json/                            — ACF field group JSON (commit to version control)