Today I’d like to share with you real-life code and schema example how one can successfully integrate GraphQL into PHP and how to optimize the result. At the moment it’s hardly unlikely to meet web-developer, who doesn’t know or doesn’t hear a thing about GraphQL. And it’s understandable: more and more developers choose to begin or continue to use it in their projects due to various advantages. Such as single end-point, extendable hierarchy, strict types, flexibility among others.
Despite the well-known fact that we are living and developing in the stackoverflow and medium era, each of us is constantly stuck with a ‘can not be googled’ type of task once in a while. Usually it happens when you want or should to create something big or try the new approach and in those cases you need as much information as one can get. The documentation always comes in handy in those cases and one begins with deep dive into it.
This step is totally where you got covered with any type of information from the official site. But theory is cheap, you are not going to be getting paid for it, right? So, on the next step, that includes taking off pink glasses and turning theory into a practice, you are totally limited by project language or framework. Today I’d like to present a solution for using GraphQL with PHP as a server side language. You are not going to hear a detailed explanation of a GraphQL documentation or PHP ports for GraphQL. What you will definitely get in detail is an example of an GraphQL integration into a PHP project, like one you can see on your screen. I mean, if someone had provided me the full example of an enterprise project three years ago, I would be more than happy. But no, it was not gonna happen then, but now I have one for you. More to it, I’m going to describe how you can solve the most common issues that you are most likely to get into and the most working ways to optimize queries.
Firstly, you should choose a GraphQL port that you’ll be working with. To fulfill that purpose, one can simply open the official GraphQL site and look for the PHP section in it. You’ll see a short list with three general-use libraries and a few of framework-use. I am actively using the first one from the WebOnyx for about three years for now and don’t have any major issues for now. They have a bit specific error handling as for me, but we solved this with a custom error handler.
In addition to the vendor library, you’ll most likely be in need for the schema documentation generator. This one seems to be a perfect fit for all GraphQL projects that I’ve worked with. It’s simple in use and setup and your schemes will be always up to date (while you run on deploy the specific command for it, of course).
Meanwhile, for a ready and working GraphQL API you’ll need:
- To design and to build request and response resolvers, design and handle schemes and data view models
- Choose the most fitting pagination invariant and integrate it in code
- Create a
ErrorHandler(it’s nothing easy in this world)
- Create endpoint
- Send a query
Let’s review each step in detail:
Well, let’s assume you have a GraphQL request parameters and know what response it should return in its body. So now you should resolve this specific request into a specific response. The main tasks of any Resolver are “query a request” and ‘return a response’, that’s why it has only four methods:
queryHere you can wrap parent vendor query method with necessary redefining of ‘resolve’ param)
createRequestIn this method we can shape the request, that we have from the endpoint by adding query variables and query body as separate data attributes
createResponseThe perfect method for initializing assemblers for the usecases (as analogy, I can describe it with calling application service). Use case in general in DDD is an Application Service. But besides it we additionally have to integrate the middle-ware layer (application validation check, do authorization check, data access check, etc.) and initialize Use Case with data. In general, we need to assemble query data into Use Case and return response from it. So, I call it
UseCaseAssembler. You can read about DDD and it’s live project implementation here.
- and the last method is an abstract method
responsethat needs to be implemented in each specific Resolver.
Well, to get a better understanding, we can get a look into specific query example. Well, you have an entity Wishlist, an object of which you need to create. Creation query may look exactly like on your screens. Each query contains fields of two types: type and arguments. The type tells us that we are going to get a Wishlist interface and structured data in response as a result. And the second field type is ‘arguments’, where you obviously have the set of input variables. You can always get a good look into input variables types on the official GraphQL site or WebOnyx as well.
Wishlist schema may look like that one. Any schema should have an interface and object that implements it. So, there are Wishlist interface and Wishlist object. The various output GraphQL types are always there: on the official site.
If GraphQL adepts talking about pagination, there is always a war. The infinite war between two clans: the first one is ‘Cursor’ and the second one is ‘Offset’. In truth, as usual with almost all wars sides, they have not so many differences in schema. The main difference will be at looking up in persistent storage. ‘Cursor’ charter looks like:
Meanwhile, there is a light at the end of a tunnel. If you roll a dice and choose a pagination side, you should create a
PageInfo schema class (it looks exactly like
WishlistSchema, that you saw before). And… understand, that method render is not gonna help us anymore, just because it fits only one data record and doesn’t have pagination in it. So, that’s a moment when you understand that you’re gonna need two types of response presenters: for one node and another one, that fits the collection of nodes.
Naturally, our frontend developers nicely ask or not so nicely demand 24/7 valid schema and fine-structured handled errors, preferably with error codes in case if something goes wrong. As you can notice from the render methods on previous screens, there is always a method
getResultFields() in each of the render method. As one can suppose from the method name, it returns the schema of general result fields. Any query result is going to have this schema. In case of any errors it will consist of ‘errors’ set of fields and you work with response respectively to it.
The result handling schema may look something like you see on display:
Also, as I explained earlier, we definitely need to return an error message and code in case of any errors, so whoever gets a response can notice that something went wrong and may handle errors accordingly with their codes. That’s how we provide the maximum possible error handling strategy and do not leave any of ‘unpredictable’ for the API user cases as a result. Your frontend engineers will thank you for that, you’ll see. It’s worse to mention, that GraphQL schema errors (like wrong type or absence of required field) are going to be catched and returned by the vendor library, so you can add your own Exception for those types of errors. It definitely helps you to get human readable exception traces.
We have two points left from the our conquer GraphQL plan: make an endpoint and send a query.
To make an endpoint it’s a specific task, I’ll explain how you can possibly do it with our case. Historically, we are using Laravel, so our endpoint is a one and only controller with two methods. The first one is for all GraphQL queries, obviously and the second one
graphQLResponse is about making a PSR7 json Response from the response data of the query.
Meanwhile, there’s only one thing left. We need to actually send a query to the endpoint URL. We are actively using Insomnia and Postman and I can recommend both of them.
Well, congratulations! You can do a full circle of GraphQL query in PHP.
Hope that you have learned some practical or theoretical things that come in handy to those who want to use GraphQL with PHP or already using it. Every cook praises his own broth.