GraphQL
GraphQL is a query language for APIs and a runtime for fulfilling those queries with your existing data. GraphQL provides a complete and understandable description of the data in your API, gives clients the power to ask for exactly what they need and nothing more, makes it easier to evolve APIs over time, and enables powerful developer tools. — graphql.org
Here is how you can create a GraphQL endpoint using Siler's simplicity powered by the PHP's GraphQL implemention.
First, let's require it:
Siler doesn't have direct dependencies, to stay fit, it favors peer dependencies, which means you have to explicitly declare a graphql
dependency in your project in order to use it.
Now let's define our Schema. We're going to use a chat-like domain:
Very simple, but if it's not familiar to you, take a look at GraphQL first since this docs will not cover what is it, but how to use it.
For each Query and Mutation we can define our resolver functions. We'll be using RedBean to help us as a simple SQLite storage ORM.
Awesome. We have type definitions and resolver functions. Let's put them together in a Schema:
Yeah, that simple! And it's exactly where Siler does it magic happen.
Thanks to webonyx/graphql-php
we can parse the schema.graphql
into an actual Schema and Siler will override the default field resolver to work with the given $resolvers
.
Now, let's create our HTTP endpoint:
That's it!
Start the server:
You can use Prima's GraphQL Playground to test it.
Here are some queries you can execute:
Query available rooms:
Yeah, there isn't any Rooms yet:
But. It's working!. Thanks to RedBean + SQLite we can play around without worrying about database setup and migrations.
Creating a new Room:
variables
Then our first room is created:
Call the query that fetches Rooms to check again:
Yup! It's there:
Without any Messages yet:
variables
No messages yet:
So let's chat!
variables
First message created:
Let's refetch our messages to check:
variables
Aha! Here we go:
Liked it? What about listening to added messages and enable real-time features? Sounds cool? That is GraphQL Subscriptions and we are going to cover next.
GraphQL Subscriptions
Here is how you can add real-time capabilities to your GraphQL applications.
Siler implementation is based on Apollo's WebSocket transport layer.
We'll need some help to get WebSockets working, so let's require two libraries.
One is for the server-side:
And the other is for the client-side:
First, let's add our Subscription type to our Schema:
Simple like that. We can subscribe to inboxes to receive new messages.
And our resolver will look like that:
Yeap, it is just resolving to the message that it receives.
But where this message comes from?
Siler powers!
Siler has a function at the Graphql
namespace to define where your subscriptions are running:
Here we are assuming that they are running at localhost on port 8080.
And why is that for?
This is just a helper that adds the Subscriptions endpoint to the Siler container so another function can actually use it when needed. And this function is publish
!
Siler\GraphQL\publish
will make a WebSocket call to the Subscriptions server notifying that something has happened.
It's first argument is the Subscription that will be triggered and the second argument is a data payload, in our case, the new message that has been created.
Our resolvers.php
will look like this:
Starting the server
As in api.php
endpoint we need to setup the Subscriptions server:
Yeah, easy like that. Let Siler do the boring stuff. You just give a Schema to the subscriptions
function. This function will return an IoServer
where you can call the run
method.
By default, Siler will run the subscriptions at localhost on port 8080.
Production-grade
To run subscriptions server at a production-grade level, please consider using some long-running process manager like Supervisor. Take a look at http://socketo.me/docs/deploy#supervisor.
That's it
No kidding.
Behind the scenes ReactPHP, Ratchet and Pawl are doing the hard work of handling WebSocket communication protocol while Siler is doing the work of adding resolver resolution to webonyx/graphql-php and handling Apollo's sub-protocol.
Ready to test?
You can use Prisma's GraphQL Playground to test the subscriptions as well.
A subscription query looks like this:
variables
The result will not be immediate since we are now listening to new messages, not querying them.
Let's take a mutation from the previous guide. Open the GraphiQL app in another tab and execute:
variables
Now go back to the subscription tab and see the update!
Awesome! Just one thing you probably have noticed. We have subscribed to all rooms. Make a test: create another Room if you haven't already and add a new message to that to see that our subscription tab, with {"roomName": "graphql"}
as variables, just got this new message. How to solve this?
Filters
Filters are part of Siler and based on Apollo's setup functions. Filter functions receives the published payload data as the first argument and the subscription variables as the second, so you can use them to perform matches:
As you can see, we have extend our Subscriptions endpoint adding filters. The filters array keys should match corresponding Subscription names, in our case: inbox
. We are just checking if the given payload room_name
is the same as the provided by the Subscription variable roomName
. Siler will perform this checks for each subscription before trying to resolve and broadcast them.
We need just a little thing to get working. Adding this room_name
field to our payload since Message only has the room_id
. At the chat resolver, before the publish, add this line:
When a RedBeanObject
is encoded to JSON it automatically converts camel case properties to underscore ones. That is why we give roomName
, but receive as room_name
.
And that should be enough to solve our problem, now you only receive data from the subscribed rooms. Enjoy!
You can see a complete example including file uploads and directives at: github.com/leocavalcante/siler/examples/graphql.
Last updated