David NĂ©grier CTO

We are pretty thrilled to announce a new GraphQL library for PHP. It is called GraphQLite and is aiming at making exposing a GraphQL API in PHP dead simple.

TL;DR

GraphQLite is a PHP library that maps your PHP classes and methods into GraphQL APIs. To do so, it relies on type-hints declared in your PHP methods (or in the PHPDoc).

The goal is to make implementing a GraphQL API easier than implementing a REST API.

Project is currently in beta and we need feedback!

Why GraphQL?

In this article, I will assume a basic level of knowledge about GraphQL. If you have never heard of GraphQL before, it is a communication protocol typically used between the browser and your server. Compared to REST:

  • it has types
  • the client is asking for the list of fields it wants

It is really very powerful and we highly recommend you to read the documentation.

The GraphQL landscape in PHP so far

GraphQL was originally developed by Facebook. Facebook offers a reference implementation of the GraphQL specification in Javascript.

This reference implementation was quickly ported to PHP.

The oldest and most used port is webonyx/graphql-php. The Webonyx team did a great work at providing a feature complete implementation of the GraphQL spec. They stayed quite close to the original JS implementation.

To be complete, there are other PHP implementations:

Using Webonyx/GraphQL-PHP

Webonyx's library is aiming at being powerful and close to the GraphQL standard, but it is not super developer friendly.

Let's take a sample with a simple "hello world" query:

query {
  echo(message: "Hello World")
}

Hello world using Webonyx/graphql-php:

<?php
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

// We declare a "Query" type used to gather queries.
$queryType = new ObjectType([
    'name' => 'Query',
    'fields' => [
        // Let's add an "echo" field
        'echo' => [
            // This is the return type of the field
            'type' => Type::string(),
            // This is the list of arguments accepted by the field
            'args' => [
                'message' => Type::nonNull(Type::string()),
            ],
            // This is the method called when resolving the field.
            'resolve' => function ($root, $args) {
                return $root['prefix'] . $args['message'];
            }
        ],
    ],
]);

For each field, I need to declare:

  • the name of the field
  • the return GraphQL type
  • the list of arguments (with their GraphQL type)
  • the "resolver"

Looking for an easier way

If I wanted to write the same "echo" method in PHP, my code would look like this:

function echo(?string $message): string
{
    return $message;
}

The PHP version is way more compact, but still, it contains all the information needed to build the GraphQL field:

      field name              Typed return
          |                        |
          |      Typed arguments   |
          |             |          |
          v             v          v
function echo(?string $message): string
{
    return $message; // <--- resolver
}

At this point, you probably know where we are heading...

What if it was possible to automatically infer the GraphQL schema from the signature of the PHP methods?

Well, it turns out it is possible!

Say hello to GraphQLite!

Your first query with GraphQLite

GraphQLite is built on top of Webonyx. It uses all the power of Webonyx but uses reflection to analyze your PHP methods and map them to a GraphQL schema automatically.

Writing the same "hello world" sample with GraphQLite is straightforward:

namespace App\Controller;

use TheCodingMachine\GraphQLite\Annotations\Query;

class MyController
{
    /**
     * @Query
     */
    function echo(?string $message): string
    {
        return $message;
    }
}

The GraphQL queries can be squattered in "controllers". When you want a PHP method to be treated as a GraphQL query, you simply add the @Query annotation on the method and you are done!

Mapping types

Of course, a real GraphQL query is usually more complex and involves dealing with types.

To declare GraphQL types, you need to:

  • add the @Type annotation to the mapped class
  • add the @Field annotation to the fields you want to expose

For instance:

namespace App\Entities;

use TheCodingMachine\GraphQLite\Annotations\Field;
use TheCodingMachine\GraphQLite\Annotations\Type;

/**
 * @Type()
 */
class Product
{
    // ...

    /**
     * @Field()
     */
    public function getName(): string
    {
        return $this->name;
    }

    /**
     * @Field()
     */
    public function getPrice(): ?float
    {
        return $this->price;
    }
}

The code above will automatically create a GraphQL type. In GraphQL type language, it would look like this:

type Product {
    name: String!
    price: Float
}

Everywhere in your code, the Product PHP class will be automatically mapped to the Product GraphQL type. I can now write queries that return the Product class:

/**
 * @Query
 */
public function product(string $id): Product
{
    // Some code that looks for a product and returns it.
}

Advanced type mapping

Mapping PHP types to GraphQL types is not always so easy.

For instance, PHP has no notion of typed arrays.

A GraphQL type like [Product!] cannot be expressed in pure PHP because PHP has no support for generics (array<Product> is not something that PHP understands natively).

In those cases, GraphQLite will use the PHPDoc to infer the correct GraphQL type:

/**
 * @Query
 * @return Product[] <== The return annotation is required to help GraphQLite
 */
public function products(): array
{
    // Some code that returns an array of Product.
}

GraphQLite can actually do a lot more. For instance it can:

More features!

The purpose of this article is not to expose all the feature we added to GraphQLite, but let's list a few:

GraphQLite can also deal with extended PHP classes

Unlike PHP, GraphQL has no notion of extends. A type cannot extend another type. However, GraphQL has the notion of interfaces and 2 types can implement a same interface.

GraphQLite will automatically create a GraphQL interface that will be shared by both types. Problem solved!

Framework agnostic

At TheCodingMachine, we love framework-agnostic code. GraphQLite is therefore framework agnostic too. You can deploy it in any framework. We are also in the process of adding framework-specific modules to help you get started quickly. So far, we have a Symfony bundle.

We plan on working on container-interop/service-provider compatibility and a Laravel module too.

Webonyx has already bindings with PSR-15 middlewares so it is relatively easy to setup a working environment with Zend Expressive or any PSR-15 enabled framework.

Possible alternatives

There are other wrappers on top of Webonyx in the PHP land.

Let's cite a few of them:

  • API Platform is a Symfony only bundle that let's you build on API. It supports both REST and GraphQL. It comes with its own set of concepts ("resources", "data providers"...) and is really one layer of abstraction above GraphQLite. My understanding is that it is a great fit if your API is "resource" oriented, or if you want both REST and GraphQL endpoints. GraphQLite is a thinner layer, closer to the GraphQL spec.
  • overblog/GraphQLBundle is a Symfony only bundle that wraps Webonyx's library too. Compared to GraphQLite, the schema is declared using YAML configuration files (instead of annotations).
  • laravel/GraphQL: a very thin layer above Webonyx
  • Laravel Lighthouse: a "Schema first" Laravel library that uses Type Language and decorators to implement the schema

Follow us

We hope you will be interested in GraphQLite.

Between the first prototype and the current version, we spent 1 year 1/2 testing and slowly improving the library. It's time for us to come out into the open and release it!

GraphQLite is currently in beta, but stable enough. We will release a stable version when the Laravel module is available, so probably in one week or two.

In the meantime, feedbacks are welcome!

You can star the project on Github (we love stars!) or follow me (@david_negrier) on Twitter for GraphQLite related news.

About the author

David is CTO and co-founder of TheCodingMachine. He is the co-editor of PSR-11, the standard that provides interoperability between dependency injection containers. David is the lead developer of Packanalyst, a website that references all PHP classes/interfaces ever stored on Packagist. He is also the lead developper of Mouf, the only graphical dependency injection framework and currently working on another PSR, regarding standardizing service providers (more containers goodness!).