Skip to main content
GraphQL Basics
CHAPTER 18 Beginner

Building GraphQL APIs with PHP

Updated: May 13, 2026
30 min read

# CHAPTER 18

Building GraphQL APIs with PHP

1. Introduction

Throughout this tutorial, we have looked at isolated snippets of PHP code. However, building a production-ready GraphQL API requires architectural organization. You cannot put your entire schema, hundreds of resolvers, and database connections into a single graphql.php file. In this chapter, we will learn how to structure a professional PHP GraphQL project, separate our concerns, manage routing, and integrate a database gracefully.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Architect a clean folder structure for a PHP GraphQL API.
  • Separate Schema definitions, Types, and Resolvers into distinct files.
  • Manage database connections and pass them via the Context object.
  • Understand how modern PHP frameworks (like Laravel) handle GraphQL.

3. Beginner-Friendly Explanation

Imagine building a car. If you try to bolt the engine, the seats, the steering wheel, and the tires all together in one giant pile, it will be a mess. You won't be able to fix it if it breaks. Instead, you build modular components: an engine block, a chassis, an interior.

In PHP, putting all your GraphQL logic in one file is the giant pile. Architecture is separating it out. We will create a folder just for "Types", a folder for "Database Models", and a central "Router" that simply accepts the HTTP request and delegates the work. This makes your code clean, readable, and highly maintainable.

4. Real-World Examples

  • Custom PHP Backend: A lightweight API built without frameworks. It uses Composer for autoloading, PDO for MySQL connections, and strict directory structures for defining GraphQL Types.
  • Laravel GraphQL (Lighthouse): In enterprise environments, developers use the Laravel framework alongside packages like Nuwave Lighthouse. Lighthouse allows you to write pure Schema SDL, and it automatically connects it to your Laravel Eloquent Database Models.
For a core PHP project, a professional structure looks like this:
text
123456789
/my-graphql-api
|-- /src
|   |-- /Types          (Defines UserType.php, PostType.php)
|   |-- /Resolvers      (Functions that fetch data)
|   |-- /Models         (Database connection classes)
|   |-- Schema.php      (Combines all Types into Root Query/Mutation)
|-- /vendor             (Composer dependencies)
|-- composer.json
|-- index.php           (The single entry point / GraphQL endpoint)

6. The Entry Point (index.php)

This file only does three things: handles HTTP, sets up Context, and executes.
php
1234567891011121314151617181920212223242526
<?php
require &#039;vendor/autoload.php';

use GraphQL\GraphQL;
use App\Schema;
use App\Models\Database;

// 1. Initialize DB
$db = new Database(&#039;localhost', 'root', '', 'graphql_db');

// 2. Set Context
$context = [
    &#039;db' => $db,
    &#039;user' => authenticateUser() // Your auth logic
];

// 3. Get Payload
$rawInput = file_get_contents(&#039;php://input');
$input = json_decode($rawInput, true);
$query = $input[&#039;query'];
$variables = $input[&#039;variables'] ?? null;

// 4. Execute
$result = GraphQL::executeQuery(Schema::get(), $query, null, $context, $variables);
echo json_encode($result->toArray());
?>

7. Defining Types in Separate Files

Instead of defining types inline, we create classes.

src/Types/UserType.php:

php
1234567891011121314151617
namespace App\Types;
use GraphQL\Type\Definition\ObjectType;
use GraphQL\Type\Definition\Type;

class UserType extends ObjectType {
    public function __construct() {
        $config = [
            &#039;name' => 'User',
            &#039;fields' => [
                &#039;id' => Type::id(),
                &#039;name' => Type::string(),
                &#039;email' => Type::string()
            ]
        ];
        parent::__construct($config);
    }
}

8. Database Integration

Resolvers should call dedicated Model classes, not run raw SQL directly inside the GraphQL configuration.

In the Resolver:

php
1234
&#039;resolve' => function($root, $args, $context) {
    // Uses the Database connection passed via Context
    return $context[&#039;db']->findUserById($args['id']);
}

9. Best Practices

  • Use PSR-4 Autoloading: Configure your composer.json to automatically load your classes from the /src directory. You should never use require_once manually for your classes.
  • Keep index.php Clean: Your entry point should be less than 50 lines of code. All complex logic should be abstracted into classes.
  • Consider a Framework: While building from scratch is great for learning, if you are building a commercial application in PHP, use a framework like Laravel or Symfony with a dedicated GraphQL package to save hundreds of hours of setup.

10. Common Mistakes

  • Circular Dependencies: When UserType requires PostType, and PostType requires UserType, PHP can get stuck in an infinite loop. The webonyx library solves this by allowing fields to be defined as a closure (an anonymous function function() { return [...] }) so they are evaluated lazily.
  • Database Logic inside Types: Never put PDO/MySQL connection credentials inside your Type files. Always pass them down via the $context.

11. Mini Exercises

  1. 1. In the proposed folder structure, which folder contains the classes that directly interact with MySQL?
  1. 2. Why is the database connection instantiated in index.php and passed into $context?

12. Coding Challenges

Challenge 1: Create the conceptual composer.json array needed to set up PSR-4 autoloading, mapping the namespace App\\ to the src/ directory.

13. MCQs with Answers

Question 1

What is the primary purpose of separating a GraphQL API into modular folders (Types, Resolvers, Models)?

Question 2

How should deeply nested Type definitions access the database connection?

Question 3

What is a popular package used to implement GraphQL in the Laravel framework?

14. Interview Questions

  • Q: Describe how you would organize the folder structure of a large-scale GraphQL application built in core PHP.
  • Q: How do you solve circular dependency issues when defining GraphQL types that reference each other?
  • Q: What is the benefit of using an established framework (like Lighthouse for Laravel) over building a GraphQL server from scratch?

15. FAQs

Q: Can I use an ORM like Doctrine or Eloquent with a standalone PHP GraphQL setup? A: Absolutely. Your resolvers don't care where the data comes from. You can easily boot up Eloquent ORM in your index.php and use it inside your resolvers.

16. Summary

In this chapter, we transitioned from basic scripting to professional software architecture. We learned how to organize a PHP GraphQL project into modular components: a single HTTP entry point (index.php), isolated Type definitions, dedicated Resolver functions, and Database models. We also discussed how the $context object is the critical glue that passes global dependencies (like the database connection) down into the deeply nested resolver tree.

17. Next Chapter Recommendation

You now have the theoretical knowledge and the architectural blueprint. It is time to build! Proceed to Chapter 19: Building a Complete GraphQL Project where we outline the steps to create a fully functional, database-driven GraphQL API.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·