A WordPress plugin that creates default GraphQL queries for NextJS sites with extensible type registration through filters.
NextJS GraphQL Hooks provides essential GraphQL queries and types that are commonly needed when building NextJS sites with WordPress as a headless CMS. The plugin follows modern PHP 8.0+ standards and uses the singleton pattern for efficient resource management.
- Default Page Fields: Automatically adds
elementorContent
andelementorCSSFile
fields to Page queries (requires Elementor) - Elementor Integration: Provides
elementorLibraryKit
query for global Elementor styles (requires Elementor) - Extensible Architecture: Use filters to register custom GraphQL types and fields
- Modern PHP 8.0+: Built with modern PHP features including typed properties, match expressions, and namespaces
- Singleton Pattern: Efficient resource management with singleton instances
- Error Handling: Comprehensive error logging and graceful fallbacks
- Auto-Update System: Automatic updates from GitHub releases
- GitHub Workflows: Automated quality checks and release management
- WordPress 6.5+
- PHP 8.0+
- WPGraphQL plugin (automatically managed as dependency)
- Elementor plugin (required for GraphQL queries)
- Full Feature Support: All plugin features are fully supported
- Automatic Dependency Management: WPGraphQL is automatically handled as a plugin dependency
- Seamless Installation: WordPress will prompt to install WPGraphQL if not already present
- No Manual Configuration: Dependencies are managed automatically by WordPress core
- Core Features Available: All GraphQL functionality works normally
- Manual Dependency Management: WPGraphQL must be installed manually
- Admin Notices: Plugin will show notices if WPGraphQL is missing
- Fallback Behavior: Plugin will not activate core features until WPGraphQL is available
- Not Recommended: This version does not meet minimum requirements
- Security Concerns: Older WordPress versions may have security vulnerabilities
- Feature Limitations: Some modern WordPress features may not be available
- Modern Language Features: Plugin uses PHP 8.0+ syntax and features
- Type Declarations: Full type safety with union types and typed properties
- Performance: Better performance with JIT compilation
- Security: Latest security features and improvements
- Syntax Errors: Plugin will not load due to PHP 8.0+ syntax
- Missing Features: Required language features not available
- End of Life: These PHP versions are no longer supported
- Required: Essential for all plugin functionality
- WordPress 6.5+: Automatically managed as dependency
- WordPress 6.0-6.4: Must be installed manually
- Minimum Version: Latest stable version recommended
- Required: Essential for all GraphQL queries and functionality
- Features Provided:
elementorContent
field - Returns Elementor page contentelementorCSSFile
field - Returns CSS file URLselementorLibraryKit
query - Global Elementor styles and kit information
- Minimum Version: Latest stable version recommended
- Note: Plugin will not function without Elementor as all GraphQL queries depend on Elementor data
Feature | WordPress 6.5+ | WordPress 6.0-6.4 | Notes |
---|---|---|---|
Auto Dependencies | ✅ Full Support | ❌ Manual Required | Core WP feature |
GraphQL Fields | ✅ Full Support | ✅ Full Support | Requires Elementor |
Auto Updates | ✅ Full Support | ✅ Full Support | GitHub integration |
Elementor Integration | ✅ Required | ✅ Required | Core functionality |
Admin Interface | ✅ Full Support | ✅ Full Support | Settings page |
Error Handling | ✅ Full Support | ✅ Full Support | Graceful fallbacks |
- WordPress 6.5+: Check if dependency installation was skipped
- WordPress 6.0-6.4: Install WPGraphQL manually
- Solution: Ensure WPGraphQL is active and up to date
- All WordPress Versions: Install and activate Elementor plugin
- Solution: Download Elementor from WordPress.org repository or Elementor website
- Note: Plugin functionality depends entirely on Elementor being active
- Check PHP Version: Must be 8.0 or higher
- Check WordPress Version: Must be 6.0 or higher (6.5+ recommended)
- Check Dependencies: Ensure both WPGraphQL and Elementor are installed and active
- WPGraphQL Status: Verify WPGraphQL is active
- Elementor Status: Verify Elementor is active and configured
- Plugin Order: Ensure NextJS GraphQL Hooks loads after both WPGraphQL and Elementor
- Cache: Clear any caching that might affect GraphQL schema
The plugin is available as a ready-to-install ZIP file from GitHub releases:
- Latest Version: 1.0.1
- Package Size: ~13KB (compressed)
- Compatibility: WordPress 6.5+ with PHP 8.0+
- Auto-Updates: Included from GitHub releases
- Dependencies: WPGraphQL and Elementor (automatically managed on WordPress 6.5+)
- Download the Plugin: Download the
nextjs-graphql-hooks-v1.0.1.zip
file from the releases page - Access WordPress Admin: Log in to your WordPress admin dashboard
- Navigate to Plugins: Go to
Plugins
→Add New
- Upload Plugin: Click the
Upload Plugin
button at the top of the page - Choose File: Click
Choose File
and select the downloaded ZIP file - Install: Click
Install Now
and wait for the upload to complete - Dependencies: WordPress will automatically prompt to install WPGraphQL and Elementor if needed (WP 6.5+)
- Activate: Click
Activate Plugin
to enable the NextJS GraphQL Hooks plugin
- Extract the ZIP: Unzip the downloaded file on your computer
- Upload via FTP: Upload the extracted
nextjs-graphql-hooks
folder to the/wp-content/plugins/
directory on your server - Activate: Go to the WordPress admin panel and activate the plugin from the
Plugins
page
If you have WP-CLI installed, you can also install the plugin using the command line:
# Download and install the plugin
wp plugin install https://github.com/SilverAssist/nextjs-graphql-hooks/releases/latest/download/nextjs-graphql-hooks-v1.0.1.zip --activate
# Or manually activate after uploading
wp plugin activate nextjs-graphql-hooks
After installation, you should see:
- Automatic Dependencies: WordPress will automatically prompt to install WPGraphQL and Elementor if not present (WordPress 6.5+)
- GraphQL Fields:
elementorContent
andelementorCSSFile
fields available in Page queries - Custom Queries:
elementorLibraryKit
query available in GraphQL - Auto-Updates: Update notifications in WordPress admin when new releases are available
- Settings Page: "GraphQL Hooks Updates" under Settings menu for manual update checks
Note: If you're using WordPress 6.5 or later, both WPGraphQL and Elementor dependencies will be automatically managed by WordPress. For older WordPress versions, you'll need to install both plugins manually.
The plugin includes an auto-update system that checks for new releases on GitHub. Updates can be installed directly from the WordPress admin panel under Dashboard → Updates.
query GetPage($id: ID!) {
page(id: $id) {
id
title
content
elementorContent(css: false)
elementorCSSFile
}
}
query GetElementorKit {
elementorLibraryKit {
kit_id
css_file
}
}
The plugin provides a comprehensive filter system that allows you to register custom GraphQL types, root queries, and fields on existing types. This makes it easy to extend the base functionality for your specific needs.
The plugin provides three main filters for extensibility:
nextjs_graphql_hooks_register_types
- Register custom GraphQL object typesnextjs_graphql_hooks_register_queries
- Register custom root query fieldsnextjs_graphql_hooks_register_fields
- Add custom fields to existing GraphQL types
Here's a real-world example showing how to implement a complete GraphQL extension in a theme or plugin. This example creates an "Advisor" system with multiple related types:
<?php
/**
* Theme GraphQL integration using NextJS GraphQL Hooks
*/
namespace YourTheme\GraphQL;
class Hooks
{
private static ?Hooks $instance = null;
public static function instance(): Hooks
{
if (!isset(self::$instance)) {
self::$instance = new self();
}
return self::$instance;
}
public function __construct()
{
// Require NextJS GraphQL Hooks plugin
if (!class_exists('NextJSGraphQLHooks\GraphQL_Hooks')) {
add_action('admin_notices', [$this, 'plugin_dependency_notice']);
return;
}
// Register our custom types through the plugin's filter system
$this->register_custom_types();
}
public function plugin_dependency_notice(): void
{
echo '<div class="notice notice-error"><p>';
echo __('Your Theme requires the NextJS GraphQL Hooks plugin to be installed and activated.', 'your-textdomain');
echo '</p></div>';
}
private function register_custom_types(): void
{
add_filter('nextjs_graphql_hooks_register_types', [$this, 'add_custom_types']);
add_filter('nextjs_graphql_hooks_register_queries', [$this, 'add_custom_queries']);
add_filter('nextjs_graphql_hooks_register_fields', [$this, 'add_custom_fields']);
}
// ... (methods shown below)
}
// Initialize the class
YourTheme\GraphQL\Hooks::instance();
/**
* Add custom types to the plugin's type registry
*
* @param array $types Existing types array
* @return array Modified types array
*/
public function add_custom_types(array $types): array
{
// Register a nested type for advisor phone information
$types['AdvisorPhone'] = [
"description" => __("Advisor Phone Type", "your-textdomain"),
"fields" => [
"phone_link" => [
"type" => "String",
"description" => __("The phone link of the advisor", "your-textdomain"),
],
"formatted_phone" => [
"type" => "String",
"description" => __("The formatted phone of the advisor", "your-textdomain"),
],
],
];
// Register a team member type
$types['AdvisorTeamMember'] = [
"description" => __("Advisor Team Member Type", "your-textdomain"),
"fields" => [
"advisor_name" => [
"type" => "String",
"description" => __("The name of the team member", "your-textdomain"),
],
"advisor_position" => [
"type" => "String",
"description" => __("The position of the team member", "your-textdomain"),
],
"advisor_image" => [
"type" => "String",
"description" => __("The image of the team member", "your-textdomain"),
],
],
];
// Register the main advisor type with complex fields
$types['Advisor'] = [
"description" => __("Advisor Type", "your-textdomain"),
"fields" => [
"name" => [
"type" => "String",
"description" => __("The name of the advisor", "your-textdomain"),
],
"slug" => [
"type" => "String",
"description" => __("The slug of the advisor", "your-textdomain"),
],
"city" => [
"type" => "String",
"description" => __("The city of the advisor", "your-textdomain"),
],
"state" => [
"type" => "String",
"description" => __("The state of the advisor", "your-textdomain"),
],
"email" => [
"type" => "String",
"description" => __("The email of the advisor", "your-textdomain"),
],
"phone" => [
"type" => "AdvisorPhone", // Reference to nested type
"description" => __("The phone of the advisor", "your-textdomain"),
],
"team_members" => [
"type" => ["list_of" => "AdvisorTeamMember"], // Array of nested types
"description" => __("The team members of the advisor", "your-textdomain"),
],
"additional_states" => [
"type" => ["list_of" => "String"], // Array of strings
"description" => __("The additional states of the advisor", "your-textdomain"),
],
],
];
return $types;
}
/**
* Add custom queries to the plugin's query registry
*
* @param array $queries Existing queries array
* @return array Modified queries array
*/
public function add_custom_queries(array $queries): array
{
// Single advisor query with arguments
$queries['advisor'] = [
"type" => "Advisor",
"description" => __("Get a single advisor by slug and name.", "your-textdomain"),
"args" => [
"slug" => [
"type" => "String",
"description" => __("The location slug of the advisor", "your-textdomain"),
"required" => true,
],
"name" => [
"type" => "String",
"description" => __("The location name of the advisor", "your-textdomain"),
"required" => true,
],
],
"resolve" => function ($_, $args) {
return $this->getAdvisor($args);
}
];
// Multiple advisors query
$queries['advisors'] = [
"type" => ["list_of" => "Advisor"],
"description" => __("Get all advisors.", "your-textdomain"),
"resolve" => function () {
return $this->getAdvisors();
}
];
return $queries;
}
/**
* Add custom fields to existing GraphQL types
*
* @param array $fields Existing fields array
* @return array Modified fields array
*/
public function add_custom_fields(array $fields): array
{
// Add event-specific fields to the Event post type
$fields['Event'] = [
"eventDate" => [
"type" => "String",
"description" => __("The date of the event", "your-textdomain"),
"resolve" => function ($post) {
$meta = get_post_meta($post->databaseId, "event_date", true);
return !empty($meta) ? $meta : null;
}
],
"startDate" => [
"type" => "String",
"description" => __("The start date of the event", "your-textdomain"),
"resolve" => function ($post) {
$meta = get_post_meta($post->databaseId, "start_date", true);
return !empty($meta) ? $meta : null;
}
],
"location" => [
"type" => "String",
"description" => __("The location of the event", "your-textdomain"),
"resolve" => function ($post) {
$meta = get_post_meta($post->databaseId, "location", true);
return !empty($meta) ? $meta : null;
}
],
];
return $fields;
}
/**
* Get a single advisor
*
* @param array $args Query arguments
* @return object|WP_Error The advisor object or error
*/
private function getAdvisor($args)
{
$slug = isset($args["slug"]) ? sanitize_text_field($args["slug"]) : "";
$name = isset($args["name"]) ? sanitize_text_field($args["name"]) : "";
if (empty($slug) && empty($name)) {
return new \WP_Error("invalid_location", "Invalid location", ["status" => 404]);
}
// Custom logic to find advisor by slug/name
$term_args = [
"taxonomy" => "category",
"hide_empty" => false,
"meta_query" => [
[
"key" => "location_data_name",
"value" => $name,
"compare" => "=",
],
],
];
$category = get_term_by("slug", $slug, "category")
?: get_terms($term_args)[0] ?? null;
if (!$category) {
return new \WP_Error("invalid_location", "Invalid location", ["status" => 404]);
}
// Return your custom advisor object
return new YourAdvisorClass($category);
}
/**
* Get all advisors
*
* @return array Array of advisor objects
*/
private function getAdvisors(): array
{
$advisors_categories = $this->getCategories();
$advisors_data = [];
foreach ($advisors_categories as $category) {
$advisor = new YourAdvisorClass($category);
if (!empty($advisor->name) && !empty($advisor->city)) {
$advisors_data[] = $advisor;
}
}
return $advisors_data;
}
After implementing the above code, you can query your custom types like this:
query GetAdvisor($slug: String!, $name: String!) {
advisor(slug: $slug, name: $name) {
name
slug
city
state
email
phone {
phone_link
formatted_phone
}
team_members {
advisor_name
advisor_position
advisor_image
}
additional_states
}
}
query GetAllAdvisors {
advisors {
name
slug
city
state
email
}
}
query GetEvent($id: ID!) {
event(id: $id) {
title
content
eventDate
startDate
location
}
}
- Always check for plugin dependency before registering filters
- Use proper namespacing to avoid conflicts
- Include comprehensive error handling in resolver functions
- Use WordPress sanitization functions for user input
- Follow GraphQL naming conventions (camelCase for fields)
- Provide detailed descriptions for all types and fields
- Test with and without the base plugin activated
For a simpler implementation directly in functions.php
:
// Add to your theme's functions.php
add_filter('nextjs_graphql_hooks_register_types', function($types) {
$types['CustomType'] = [
"description" => "A custom type",
"fields" => [
"custom_field" => [
"type" => "String",
"description" => "A custom field",
],
],
];
return $types;
});
add_filter('nextjs_graphql_hooks_register_queries', function($queries) {
$queries['customQuery'] = [
"type" => "CustomType",
"description" => "Get custom data",
"resolve" => function() {
return ['custom_field' => 'Custom value'];
}
];
return $queries;
});
This implementation pattern ensures your GraphQL extensions work seamlessly with the NextJS GraphQL Hooks plugin while maintaining clean, maintainable code.
The plugin provides several helper methods that can be used when extending functionality:
Register a custom GraphQL object type.
- $type_name: String name of the GraphQL type
- $config: Array with 'description' and 'fields' keys
Add custom fields to an existing GraphQL type.
- $type_registry: The WPGraphQL TypeRegistry instance
- $type_name: String name of the existing type to extend
- $fields: Array of field configurations
Register a field in the root query.
- $type_registry: The WPGraphQL TypeRegistry instance
- $field_name: String name of the query field
- $config: Array with type, description, args, and resolve keys
Instead of using the helper methods directly, the recommended approach is to use the filter system:
Filter to register custom GraphQL object types.
add_filter('nextjs_graphql_hooks_register_types', function($types) {
$types['YourType'] = [/* type config */];
return $types;
});
Filter to register custom root query fields.
add_filter('nextjs_graphql_hooks_register_queries', function($queries) {
$queries['yourQuery'] = [/* query config */];
return $queries;
});
Filter to add fields to existing types.
add_filter('nextjs_graphql_hooks_register_fields', function($fields) {
$fields['ExistingType'] = [/* field configs */];
return $fields;
});
nextjs-graphql-hooks/
├── nextjs-graphql-hooks.php # Main plugin file
├── includes/
│ └── GraphQL_Hooks.php # GraphQL hooks class
├── languages/ # Translation files
└── README.md # This file
nextjs_graphql_hooks_loaded
- Fired when the plugin is fully loadednextjs_graphql_hooks_register_types
- Use this to register custom GraphQL types
// Add this to your theme's functions.php file
add_action('nextjs_graphql_hooks_register_types', function ($type_registry, $hooks_instance) {
// Your custom type registration here
// See examples/custom-types-example.php for detailed examples
}, 10, 2);
The plugin includes comprehensive error handling:
- Graceful fallbacks when Elementor is not available
- Error logging for debugging purposes
- Empty string returns instead of exceptions for missing content
- Double quotes for all strings:
"string"
not'string'
- Short array syntax:
[]
notarray()
- Namespaces:
NextJSGraphQLHooks
- Singleton pattern:
Class_Name::get_instance()
- WordPress hooks:
\add_action("init", [$this, "method"])
- PHP 8+ Features: Match expressions, array spread, typed properties
- Global function calls: Use
\
prefix for WordPress functions in namespaced context
- Namespace Organization: Clean namespace structure
- Match Expression: For conditional logic
- Array Spread: Efficient array building
- Typed Properties: Strong typing with nullable types
- Return Type Declarations: Explicit return types
- Null Coalescing Operator: Safe null handling
- Follow the established coding standards
- Add proper PHPDoc comments
- Include error handling
- Test with both Elementor enabled and disabled
- Ensure compatibility with WPGraphQL latest version
- Quality Checks: Automated code validation and WordPress standards
- Size Check: Package size monitoring for pull requests
- Release: Automated release creation from version tags
Polyform Noncommercial License 1.0.0
Silver Assist
Website: http://silverassist.com/
This project is licensed under the Polyform Noncommercial License 1.0.0 for noncommercial use only.
For support and feature requests, please contact Silver Assist or open an issue in the project repository.
See CHANGELOG.md for detailed version history and changes.
Made with ❤️ by Silver Assist