Skip to main content
GraphQL Basics
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
1234567891011121314
CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(100) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL
);

CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    is_completed BOOLEAN DEFAULT FALSE,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

5. The GraphQL Schema Blueprint

Before coding PHP, we define our API contract.
graphql
12345678910111213141516171819202122232425262728293031
type User {
  id: ID!
  name: String!
  email: String!
  tasks: [Task!]!
}

type Task {
  id: ID!
  title: String!
  isCompleted: Boolean!
  owner: User!
}

type AuthPayload {
  token: String!
  user: User!
}

type Query {
  me: User
  myTasks: [Task!]!
}

type Mutation {
  register(name: String!, email: String!, pass: String!): AuthPayload
  login(email: String!, pass: String!): AuthPayload
  createTask(title: String!): Task
  toggleTask(id: ID!, isCompleted: Boolean!): Task
  deleteTask(id: ID!): Boolean
}

6. Phase 1: Authentication Logic

  1. 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.
  1. 2. Login Mutation: The resolver looks up the email. Uses password_verify(). If valid, generates a JWT.
  1. 3. Context Injection: In your index.php, read the HTTP Authorization header. Decode the JWT. If valid, set $context['userId'] = $decodedId;.

7. Phase 2: Queries (Reading Data)

  1. 1. me Resolver: Check if $context['userId'] exists. If yes, SELECT * FROM users WHERE id = ?. Return the row.
  1. 2. myTasks Resolver: Ensure user is logged in. SELECT * FROM tasks WHERE user_id = ?. Return the array of tasks.

8. Phase 3: Resolving Relationships

When a client queries myTasks { owner { name } }, the owner resolver inside the Task type needs to function.
  • Task -> owner: Take the $root['user_id'] and run SELECT * FROM users WHERE id = ?.

9. Phase 4: Mutations (Modifying Data)

  1. 1. createTask: Ensure logged in. INSERT INTO tasks (title, user_id) VALUES (?, ?). Fetch the newly created task and return it.
  1. 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 = ?.
  1. 3. deleteTask: Same security check as above. DELETE FROM tasks WHERE id = ?. Return true.

10. Testing the Application

Use GraphQL Playground to test your API.
  1. 1. Run the register mutation. Copy the Token.
  1. 2. Add the token to the Playground's HTTP Headers: {"Authorization": "Bearer YOUR_TOKEN"}
  1. 3. Run the createTask mutation.
  1. 4. Run the myTasks query.
  1. 5. Create a second user and try to deleteTask the first user's task. Verify that the server rejects it.

11. Mini Exercises

  1. 1. In Phase 4, why must we check $task['user_id'] === $context['userId'] before deleting?
  1. 2. If the myTasks query returns an array of 5 tasks, how many times will the owner resolver execute if the client asks for the owner's name? (Hint: Think about N+1).

12. Coding Challenges

Challenge 1: Expand the application. Add a Category 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.

13. Summary

This chapter served as your roadmap to building a complete, production-ready GraphQL API. We designed a relational database, crafted a strictly typed schema, implemented JWT authentication, and structured our resolvers to handle relationships and enforce strict security checks. Building this project locally will solidify everything you have learned in the previous 18 chapters.

14. Next Chapter Recommendation

Are you ready to test your knowledge or prepare for a job interview? Proceed to Chapter 20: GraphQL Interview Questions and Practice Challenges to review core concepts, tackle advanced coding exercises, and solidify your mastery of GraphQL.

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: ·