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:
- youshido-php/GraphQL was quite complete, but development was halted due to a lack of maintainers
- digiaonline/graphql: the new kid on the block, in heavy development. Looks promising.
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:
- Infer union types from the PHPDoc
- Map DateTimeImmutable natively
- Deal with file uploads by maping the PSR-7 UploadedFileInterface
More features!
The purpose of this article is not to expose all the feature we added to GraphQLite, but let's list a few:
- You can declare mutations with the
@Mutation
annotation - You can declare input types with the
@Factory
annotation - You can extend a type with resolvers stored in services
- You can deal with security (authentication and authorization)
GraphQLite can also deal with extended PHP classes
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 and WorkAdventure. He is the co-editor of PSR-11, the standard that provides interoperability between dependency injection containers. He is also the lead developper of GraphQLite, a framework-agnostic PHP library to implement a GraphQL API easily.