A PHP port of GraphQL reference implementation

Last update: May 20, 2022


CI Coverage Status Latest Stable Version

This is a PHP implementation of the GraphQL specification based on the reference implementation in JavaScript.


Via composer:

composer require webonyx/graphql-php


Full documentation is available at https://webonyx.github.io/graphql-php or in the docs directory.

If you don't know what GraphQL is, visit the official website first.


There are several ready examples in the examples directory, with a specific README file per example.


This project follows Semantic Versioning 2.0.0.

Elements that belong to the public API of this package are marked with the @api PHPDoc tag. Those elements are thus guaranteed to be stable within major versions. All other elements are not part of this backwards compatibility guarantee and may change between minor or patch versions.


This project exists thanks to all the people who contribute. [Contribute].



Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [Become a sponsor]




  • 1. Lazy loading of types

    Implements #425 Proof of concept of full support for lazy loading of types.

    In a nutshell, you can substitute a callable any place that you would ordinarily supply a Type, and it won't be executed by the library until it is actually needed.

    For example, you can create a Types class that looks like this:

    class Types {
    	public static function get(string $classname)
    		return function() use ($classname) {
    			return static::fromCache($classname);
    	protected static function fromCache(string $classname) {
    		$parts = explode("\\", $classname);
    		$cacheName = strtolower(preg_replace('~Type$~', '', $parts[count($parts) - 1]));
    		$type = null;
    		if (!isset(self::$types[$cacheName])) {
    			if (class_exists($classname)) {
    				$type = new $classname();
    			self::$types[$cacheName] = $type;
    		$type = self::$types[$cacheName];
    		if (!$type) {
    			throw new Exception("Unknown graphql type: " . $classname);
    		return $type;
        public static function currency() { return static::get(CurrencyType::class);}
        // ... I explicitly stub out these type methods for the benefit of intellisense and [stanning](https://github.com/phpstan/phpstan), but they're not necessary

    And then your field/arg definitions look the same as usual:

    	'currency' => [
    		'type' => Types::currency(), // this won't load until it is specifically needed!
    		'description' => 'A currency record',

    It may be difficult to imagine this being of any appreciable benefit for small or toy programs, but in my real project I dynamically generate my mutation types (using this) and doing even a small mutation triggers a flood of activity and the instantiation of 656 types(!).

    With this change, that number drops to 1.

    I have run this in the profiler: without lazy loading with lazy loading

    For me, this equates to a savings of around a quarter of a second and 3MB of memory.

    So, what do you think? It's only around 50 lines of mostly trivial code. If you think it looks promising I'll start in on tests and documentation and further polishing.

    Reviewed by shmax at 2019-10-17 05:32
  • 2. Implement PSR-7 RequestInterface support

    ServerRequestInterface is not needed, parent RequestInterface is sufficient
    Replaced custom PSR-7 test stubs with nyholm/psr-7 implementation
    Fixed reading body contents

    • ServerRequestInterface extends RequestInterface so for this case I think it's not a BC break.
    • Rephrasing exception message might be considered a BC break I guess.
    Reviewed by simPod at 2019-12-13 14:48
  • 3. Executor performance optimization

    Hi. First, thank you for the great library! We use it to power our API at Scuk.cz and it's been a smooth ride.

    Recently, however, we've discovered poor performance for some of the queries the frontend service sends to the GraphQL endpoint. Over time our schema got pretty big (currently it consists of ~300 types /half of which are object types/ and ~1000 resolvable fields on object types). So did the queries (one of the pages in our app queries ~200 fields).

    We successfully refactored GraphQL schema so that types & fields are created lazily. Also we started caching parsed AST. This got us nice speed improvements. After these I've started to look at performance of the Executor. It costs us more than 150ms per request for some queries. This is the call graph of GraphQL endpoint for such query (library calls are highlighted):

    GraphQL call graph

    I've started digging in the code and found some easy to improve functions (e.g. Executor::shouldIncludeNode()). But I estimate these would shave off only couple of milliseconds. So I've started to work on a new executor:

    • separated compilation & execution phases
      • first, AST is transformed into an intermediate representation (= instructions) that is better suitable for manipulation and exection
      • then, the instructions are processed sequentially (I suppose sequential execution should be more machine-friendly than jumping between tens of functions and callbacks, therefore perform better)
    • instruction pipeline can be manipulated with during the execution (new instructions are pushed to the front or back)
    • compiled instructions could be cached instead of an AST
      • in the future, they could be compiled down to PHP code for even better performance (as some templating engines, DI containers etc. do)

    This is still a work in progress and will need some time before the new executor passes the test suite. Yet, what do you think about this? :)

    Questions & notes:

    1. ValueNode interface doc comment says that VariableNode is a ValueNode. But VariableNode does not implement it. Is it intentional, or a bug?
    2. This might be quite a lot of changes. Is there a way to check code style? (I've run composer run lint, however, PHP-CS reported issues even with current files.)
    3. Current executor works with results as arrays. But this is a problem for JSON serialization if the array remains empty (there is a workaround converting empty array to stdClass). I started new executor to work with stdClasses instead, because I need pass by reference semantics, also it fixes JSON-serialization-related issue from the start. But this would be big breaking change. Should I continue with stdClasses, or use arrays?
    4. I started typehinting all parameters of all new methods. But then I realized the library still supports older PHP versions. Is there a plan to drop old versions support?
    5. ResolveInfo is tightly coupled to AST traversal execution model. After the compilation, AST is no longer relevant for new executor. It could include needed AST nodes in instructions, however, it defies the point of the compilation step.
    Reviewed by jakubkulhan at 2018-07-26 09:19
  • 4. Add support for setting extensions from resolvers.

    This adds an API to ResolveInfo which allows resolvers to directly add items to be returned to the client in the extensions element of the response body.

    see http://facebook.github.io/graphql/October2016/#sec-Response-Format

    Reviewed by aelnagger at 2017-11-02 14:30
  • 5. Fix namenode type / rewrite Printer

    An alternative fix for #655, the infamous NamedTypeNode NameNode / string debacle.

    Took me a while to understand, but all the trouble was being caused by the unusual algorithm used to print ASTs. It tied into Visitor::visit, and starting at the leaf nodes, would stomp over properties with strings so that it could concatenate everything into a big string. We noticed this happening on the NamedTypeNode in particular, but actually this was happening with all of the nodes.

    Once I understood what was happening, I was able to rearrange the algorithm into a more conventional top-down recursive one that does not "edit" any nodes, nor do any illegal writes. While I was at it, I corrected a number of type annotations. PHPStan is now happy. Note that this change is not as dramatic as it might look; all of the callbacks from the old leave section in Printer were simply ported into a big switch statement. I didn't change any of the core logic.

    Interestingly, performance seems to improve with these changes, though I'm not sure why.

    master: https://blackfire.io/profiles/a1b07e4f-cba2-4327-84c5-c42e7559d8b2/graph

    this branch: https://blackfire.io/profiles/755fec5e-3bf3-491a-b8d0-8a6d78a30fd6/graph

    Reviewed by shmax at 2020-07-03 22:08
  • 6. Horizontal eval for lazy loading same-type objects

    When I first learned about GraphQL there was a performance chapter. If every relation loads every single relation, it requires many queries to load a few levels of data, even if the actual data is only on the last level. The solution was to lazy load objects: first collect ids (horizontal), and then load all objects at once, and then evaluate the next level.

    Just in case. This query:

    query {
        user {
            reservations {
                resource {
                    sport {

    would take 20 queries to find 10 reservations > 10x 1 resource > 10x 1 sport. If it would eval horizontally, it would do 2 queries: load 10 resources, then load 10 sports. (Maybe not 2 levels, but even 1 could be a big difference.)

    Is that a thing? It seems like the query is evaled vertically first, so it loads the entire 1 reservation (1 resource + 1 sport), and then the entire 1 next (1 resource + 1 sport) etc.

    Reviewed by rudiedirkx at 2016-10-21 22:19
  • 7. Update graphql spec to April2016

    There is a new version of the GraphQL spec April2016.

    It changed some parts in the introspection. I might have time to open a PR for this, but probably at earliest in two weeks.

    Reviewed by danez at 2016-04-22 07:55
  • 8. Add AmpPromiseAdapter

    Had some time today so I implemented a PromiseAdapter for Amp. Note that I just wrote blindly what seemed to make some sense and it's not tested at all. I'll try to use it in my project when I get to reimplementing my GraphQL part to async. And of course I need to add some unit tests. All of this might take a while (a few months), I'm just posting this here in case someone was looking for it and was willing to help me out with testing.

    Code reviews are of course welcome.

    Reviewed by enumag at 2019-10-03 21:16
  • 9. Feedback on Experimental Executor

    Please post your feedback here on the new experimental executor introduced in https://github.com/webonyx/graphql-php/pull/314

    Especially interested in following questions:

    1. Do you see a performance improvement on your queries? How much on average?
    2. Do you experience any issues / unexpected behavior with it?
    Reviewed by vladar at 2018-11-21 07:34
  • 10. Mutations with Relay (React)

    I'm using the laravel-graphql, and created an issue but I'm not sure if it's something that should actually be created here (still trying to wrap my head around GraphQL).

    When working with Relay, it expects that mutations have only one argument called input. Looking at the todo example, in the schema.json file it has that field listed as an INPUT_OBJECT type. I created the following (again, using laravel-graphql), but I'm unable to get the fields from the InputObjectType. Am I doing something wrong?

    namespace App\GraphQL\Mutations;
    use GraphQL;
    use App\Models\Customer;
    use GraphQL\Type\Definition\Type;
    use GraphQL\Type\Definition\InputObjectType;
    use Folklore\GraphQL\Support\Mutation;
    class UpdateCustomerEmail extends Mutation
         * Associated GraphQL Type.
         * @return mixed
        public function type()
            return GraphQL::type('customerPayload');
         * Available arguments for mutation.
         * @return array
        public function args()
            return [
                'input' => [
                    'name' => 'input',
                    'type' => new InputObjectType([
                        'name' => 'updateCustomerEmailInput',
                        'fields' => [
                            'entity_id' => [
                                'name' => 'entity_id',
                                'type' => Type::nonNull(Type::string())
                            'email' => [
                                'name' => 'email',
                                'type' => Type::nonNull(Type::string())
         * Resolve mutation.
         * @param  string $root
         * @param  array $args
         * @return Customer
        public function resolve($root, $args, $info)
            // $root is null, $args is an empty array and nothing in $info gives me the 
            // variables passed in the request
            $customer = Customer::find($args['input']['entity_id']); // $args['input'] is not set
            $customer->email = $args['input']['email'];
            return $customer;
    Reviewed by chrissm79 at 2015-11-11 22:16
  • 11. Performance improvements

    I've been messing around with this project a bit lately, and I've had some trouble with performance. The GraphQL executor does quite a bit of unnecessary work when it resolves fields. Since it operates recursively, larger lists have a really bad impact on performance. I have tried to fix some of the bottlenecks by memoizing objects and values that can be re-used.

    I'd like to hear what you think about this approach. According to the Blackfire profiler the response was 2x faster for a large query and 1.5x faster for one I use in my app. You can check out my fork here: https://github.com/johanobergman/graphql-php

    Reviewed by johanobergman at 2015-10-18 20:17
  • 12. Allow input object types values to be converted to value objects

    When working with mutations you often want to use an input object type. Currently, the provided input values are passed as an array to the resolver. Working with arrays is tedious and error prone. I'd rather have real / typed objects to work with.

    It would be great if I could add a parseValue method to my InputObjectType configuration so that it can convert to a real object instead.

    Something like this:

    class Tag {
        public function __construct(
            public readonly string $name,
            public readonly string $value,
        ) {}
    $tag = new InputObjectType([
        'name' => 'Tag',
        'fields' => [
            'name' => [
                'type' => Type::string(),
            'value' => [
                'type' => Type::string(),
        'parseValue' => function(array $values) {
            return new Tag(
    class StoryFiltersInput {
        public function __construct(
            public readonly string $author,
            public readonly boolean $popular,
            public readonly Tag $tag,
        ) {}
    $input = new InputObjectType([
        'name' => 'StoryFiltersInput',
        'fields' => [
            'author' => [
                'type' => Type::id(),
            'popular' => [
                'type' => Type::boolean(),
            'tag' => [
                'type' => $tag,
        'parseValue' => function(array $values) {
            return new StoryFiltersInput(

    As you can see, by the time the StoryFiltersInput is constructed, the $values['tag'] is already parsed to an instance of Tag.

    I checked to see if I could add this, and I think it should happen somewhere here: https://github.com/webonyx/graphql-php/blob/cc1ce0572461dec9bb75bbc48cb7eb0049ae3b2e/src/Utils/Value.php#L48

    @spawnia Do you think this is something that can be added? I can try to create a PR and see how far I come.

    Reviewed by ruudk at 2022-05-23 12:47
  • 13. Never resolve additional types in Schema constructor

    When you construct a Schema and pass an array of type callables like this:

    Schema(['types' => [
      'TypeName' => function() { },

    the callables are immediately resolved in the constructor.

    If you want the types to not be resolved, you must use a callable that returns the types array with callables like this:

    Schema(['types' => fn() => [
      'TypeName' => function() { },

    I was surprised by that because I thought my array of callables was already enough for lazy loading.

    When looking at the source code I find the distinction a bit weird and I think it can be handled in the same way.

    When Schema::getTypeMap is called, it will call Schema::collectAllTypes which will call Schema::resolveAdditionalTypes.

    Reviewed by ruudk at 2022-05-04 11:12
  • 14. Dependency Dashboard

    This issue provides visibility into Renovate updates and their statuses. Learn more

    Ignored or Blocked

    These are blocked by an existing closed PR and will not be recreated unless you click a checkbox below.

    Detected dependencies

    • php ^7.4 || ^8
    • ext-json *
    • ext-mbstring *
    • amphp/amp ^2.6
    • dms/phpunit-arraysubset-asserts ^0.4
    • ergebnis/composer-normalize ^2.16
    • mll-lab/php-cs-fixer-config ^4.4
    • nyholm/psr7 ^1.4
    • phpbench/phpbench ^1.2
    • phpstan/extension-installer ^1.1
    • phpstan/phpstan 1.6.9
    • phpstan/phpstan-phpunit 1.1.1
    • phpstan/phpstan-strict-rules 1.2.3
    • phpunit/phpunit ^9.5
    • psalm/plugin-phpunit ^0.16.1
    • psr/http-message ^1
    • react/promise ^2
    • symfony/polyfill-php81 ^1.23
    • symfony/var-exporter ^5.3
    • thecodingmachine/safe ^1.3
    • vimeo/psalm ^4.19
    • actions/checkout v3
    • shivammathur/setup-php v2
    • stefanzweifel/git-auto-commit-action v4
    • actions/checkout v3
    • creyD/prettier_action v4.2
    • actions/checkout v3
    • shivammathur/setup-php v2
    • stefanzweifel/git-auto-commit-action v4
    • actions/checkout v3
    • shivammathur/setup-php v2
    • ramsey/composer-install v2
    • actions/upload-artifact v3
    • actions/checkout v3
    • actions/download-artifact v3
    • codecov/codecov-action v3
    • actions/checkout v3
    • shivammathur/setup-php v2
    • ramsey/composer-install v2
    • actions/checkout v3
    • mhausenblas/mkdocs-deploy-gh-pages 1.24
    • actions/checkout v3
    • shivammathur/setup-php v2
    • ramsey/composer-install v2
    • actions/checkout v3
    • shivammathur/setup-php v2
    • ramsey/composer-install v2

    • [ ] Check this box to trigger a request for Renovate to run again on this repository
    Reviewed by renovate[bot] at 2022-04-25 07:40
  • 15. Avoid eager-loading schema unnecessarily

    Resolves https://github.com/webonyx/graphql-php/issues/1093 Resolves https://github.com/webonyx/graphql-php/issues/954 Resolves https://github.com/webonyx/graphql-php/pull/1137

    Reviewed by spawnia at 2022-04-07 15:47
Monorepo of the PoP project, including: a server-side component model in PHP, a GraphQL server, a GraphQL API plugin for WordPress, and a website builder
Monorepo of the PoP project, including: a server-side component model in PHP, a GraphQL server, a GraphQL API plugin for WordPress, and a website builder

PoP PoP is a monorepo containing several projects. The GraphQL API for WordPress plugin GraphQL API for WordPress is a forward-looking and powerful Gr

May 8, 2022
Pure PHP implementation of GraphQL Server – Symfony Bundle

Symfony GraphQl Bundle This is a bundle based on the pure PHP GraphQL Server implementation This bundle provides you with: Full compatibility with the

Jan 15, 2022
GraPHPinator ⚡ 🌐 ⚡ Easy-to-use & Fast GraphQL server implementation for PHP

Easy-to-use & Fast GraphQL server implementation for modern PHP. Includes features from latest draft, middleware directives and modules with extra functionality.

Apr 27, 2022
GraphQL implementation with power of Laravel

Laravel GraphQL Use Facebook GraphQL with Laravel 5.2 >=. It is based on the PHP implementation here. You can find more information about GraphQL in t

Mar 9, 2022
Test your PHP GraphQL server in style, with Pest!

Pest GraphQL Plugin Test your GraphQL API in style, with Pest! Installation Simply install through Composer! composer require --dev miniaturebase/pest

Sep 19, 2021
Pure PHP realization of GraphQL protocol
Pure PHP realization of GraphQL protocol

Looking for Maintainers! Unfortunatelly, we cannot longer support this package and are looking for someone to take the ownership. Currently Only PRs w

May 1, 2022
Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time.
Create REST and GraphQL APIs, scaffold Jamstack webapps, stream changes in real-time.

API Platform is a next-generation web framework designed to easily create API-first projects without compromising extensibility and flexibility: Desig

May 19, 2022
This bundle provides tools to build a complete GraphQL server in your Symfony App.

OverblogGraphQLBundle This Symfony bundle provides integration of GraphQL using webonyx/graphql-php and GraphQL Relay. It also supports: batching with

May 23, 2022
GraphQL Bundle for Symfony 2.

Symfony 2 GraphQl Bundle Use Facebook GraphQL with Symfony 2. This library port laravel-graphql. It is based on the PHP implementation here. Installat

Nov 1, 2020
Laravel wrapper for Facebook's GraphQL

Laravel GraphQL Use Facebook's GraphQL with Laravel 6.0+. It is based on the PHP port of GraphQL reference implementation. You can find more informati

May 18, 2022
A framework for serving GraphQL from Laravel
A framework for serving GraphQL from Laravel

Lighthouse A framework for serving GraphQL from Laravel Lighthouse is a GraphQL framework that integrates with your Laravel application. It takes the

May 18, 2022
EXPERIMENTAL plugin extending WPGraphQL to support querying (Gutenberg) Blocks as data, using Server Side Block registries to map Blocks to the GraphQL Schema.

WPGraphQL Block Editor This is an experimental plugin to work toward compatiblity between the WordPress Gutenberg Block Editor and WPGraphQL, based on

Apr 26, 2022
🍞🧑‍🍳 An on-the-fly GraphQL Schema generator from Eloquent models for Laravel.

An on-the-fly GraphQL Schema generator from Eloquent models for Laravel. Installation Quickstart Model schemas Installation This package requires PHP

Apr 15, 2022
Add Price Including tax for Magento's "cart" GraphQl query

Comwrap_GraphQlCartPrices Add Price Including tax for Magento's "cart" GraphQl query Query will looks like following: items { id __typenam

Dec 2, 2021
A Statamic Pro addon that provides alternative GraphQL queries for collections, entries and global sets.

Statamic Enhanced GraphQL A Statamic CMS GraphQL Addon that provides alternative GraphQL queries for collections, entries and global sets. ⚠️ This is

Dec 7, 2021
Place where I record all knowledge gained for GraphQL from Laracasts & other tutorials.

Knowledge from Laracasts series: https://laracasts.com/series/graphql-with-laravel-and-vue What is GraphQL It is a query language for your API, and it

Dec 26, 2021
The server component of API Platform: hypermedia and GraphQL APIs in minutes

API Platform Core API Platform Core is an easy to use and powerful system to create hypermedia-driven REST and GraphQL APIs. It is a component of the

May 20, 2022
YouTrack RestAPI library for PHP. An implementation for communicating with your YouTrack instance.
YouTrack RestAPI library for PHP. An implementation for communicating with your YouTrack instance.

YouTrack API PHP This is an implementation for communicating with the JetBrains YouTrack RestAPI. This library covers basic resource calls available i

May 3, 2022
Shopware PHP SDK is a simple SDK implementation of Shopware 6 APIs
Shopware PHP SDK is a simple SDK implementation of Shopware 6 APIs

Shopware PHP SDK is a simple SDK implementation of Shopware 6 APIs. It helps to access the API in an object-oriented way.

May 24, 2022