CHAPTER 19
Beginner
Building a Complete GraphQL Project
Updated: May 13, 2026
45 min read
# CHAPTER 19
Building a Complete GraphQL Project
1. Introduction
Welcome to the Capstone Project! You have learned the syntax, the theories, the security protocols, and the architecture. In this chapter, we will outline a complete, real-world project: A Task Management API. This project will require you to integrate Core PHP, MySQL, Authentication, and complete CRUD operations using GraphQL Queries and Mutations.2. Learning Objectives
By the end of this project, you will have:- Designed a complete MySQL database schema for Users and Tasks.
- Written a comprehensive GraphQL Schema (SDL).
- Implemented user registration and JWT authentication (Login).
- Created Queries to fetch lists and relational data.
- Created Mutations to Create, Update, and Delete data.
3. Project Overview
The Application: A Task Manager (To-Do List). Features:- Users can register and log in.
- Authenticated users can create new tasks.
- Users can view their own tasks (but not other users' tasks).
- Users can mark tasks as completed.
- Users can delete tasks.
4. Database Setup (MySQL)
First, we need our tables.
sql
5. The GraphQL Schema Blueprint
Before coding PHP, we define our API contract.
graphql
6. Phase 1: Authentication Logic
-
1.
Register Mutation: Your PHP resolver accepts the name, email, and password. It uses
password_hash()to hash the password, inserts the user into MySQL, generates a JWT, and returns it.
-
2.
Login Mutation: The resolver looks up the email. Uses
password_verify(). If valid, generates a JWT.
-
3.
Context Injection: In your
index.php, read the HTTPAuthorizationheader. Decode the JWT. If valid, set$context['userId'] = $decodedId;.
7. Phase 2: Queries (Reading Data)
-
1.
meResolver: Check if$context['userId']exists. If yes,SELECT * FROM users WHERE id = ?. Return the row.
-
2.
myTasksResolver: Ensure user is logged in.SELECT * FROM tasks WHERE user_id = ?. Return the array of tasks.
8. Phase 3: Resolving Relationships
When a client queriesmyTasks { owner { name } }, the owner resolver inside the Task type needs to function.
-
Task -> owner: Take the
$root['user_id']and runSELECT * FROM users WHERE id = ?.
9. Phase 4: Mutations (Modifying Data)
-
1.
createTask: Ensure logged in.INSERT INTO tasks (title, user_id) VALUES (?, ?). Fetch the newly created task and return it.
-
2.
toggleTask: Security Check! First, fetch the task. Check if$task['user_id'] === $context['userId']. If they don't match, throw an unauthorized error. If they match,UPDATE tasks SET is_completed = ? WHERE id = ?.
-
3.
deleteTask: Same security check as above.DELETE FROM tasks WHERE id = ?. Returntrue.
10. Testing the Application
Use GraphQL Playground to test your API.-
1.
Run the
registermutation. Copy the Token.
-
2.
Add the token to the Playground's HTTP Headers:
{"Authorization": "Bearer YOUR_TOKEN"}
-
3.
Run the
createTaskmutation.
-
4.
Run the
myTasksquery.
-
5.
Create a second user and try to
deleteTaskthe first user's task. Verify that the server rejects it.
11. Mini Exercises
-
1.
In Phase 4, why must we check
$task['user_id'] === $context['userId']before deleting?
-
2.
If the
myTasksquery returns an array of 5 tasks, how many times will theownerresolver execute if the client asks for the owner's name? (Hint: Think about N+1).
12. Coding Challenges
Challenge 1: Expand the application. Add aCategory table to MySQL. Update the Schema so a Task belongs to a Category. Add a query to fetch all categories, and update createTask to accept a categoryId.