GraphQL gets a lot of hype because it lets the frontend ask for exactly the fields it needs—no more, no less. That sounds neat. But it also comes with rules, schemas, resolvers, and an entire layer you now have to learn and maintain on top of your backend.

Here’s the thing: if you’re already a Laravel developer, you don’t need GraphQL at all. With DTOs (Data Transfer Objects) and the AQC (Atomic Query Construction) pattern, you can achieve the same precision without adding another language to your stack.

How GraphQL Works (and Why It Feels Heavy)

1. Frontend initializes the query

In GraphQL, the frontend decides which fields it wants:

    

                                {
  users {
    id
    name
    email
  }
}

                            

The request itself defines the data shape.

Backend goes through the GraphQL layer

Every request has to be parsed, validated, and resolved according to GraphQL’s schema. Developers must learn and respect GraphQL’s conventions, resolvers, and quirks.

3. Response is always JSON

The backend returns JSON shaped exactly like the query asked for.

That’s powerful, but it’s also extra machinery that Laravel developers don’t necessarily need.


The Laravel-Native Alternative

Now let’s contrast this with a DTO + AQC approach.

1. Frontend makes a request (but not the query)

The frontend doesn’t decide the fields. It just hits an API endpoint—simple and familiar.

2. Backend uses Laravel patterns you already know

In your controller or repository, you fetch data the same way you always have—through Eloquent models, repositories, or AQC classes. Nothing alien here. The difference is that you pipe results into DTO classes.

                                class BasicUser extends BaseDTO {
    public int $id;
    public string $name;
    public string $email;
}

                            

DTOs act as strict contracts. Only the fields you define here get selected and returned, no matter how messy your models are.

BaseDTO class

                                abstract class BaseDTO
{
    public static function columns(): array
    {
        return array_map(
            fn($prop) => $prop->getName(),
            (new \ReflectionClass(static::class))->getProperties()
        );
    }
}
                            

And here's how to select with eloquent.

                                class UserController extends Controller
{
    public function index(Request $request)
    {
        $users = User::select(BasicUser::columns())->get();

        if($request->ajax()){
            return response()->json($users);
        }

        return view('user.index', ['users' => $users]);
    }
}
                            

Here's an example with the AQC design pattern.

                                namespace App\AQC\User;

use App\Models\User;

class GetUsers
{
    public static function handle($params = [], $paginate = true, $columns = '*')
    {
        $userObj = User::latest('id');

        if (isset($params['is_active']) && $params['is_active'] > 0) {
            $userObj->where('is_active', $params['is_active']);
        }

        // add more conditions for different use cases

        $userObj->select($columns);

        return $paginate
            ? $userObj->paginate(User::PAGINATE)
            : $userObj->get();
    }
}
                            

3. Response can be JSON or HTML

Unlike GraphQL, you’re not locked to JSON. Sometimes JSON makes sense:

                                return response()->json($users);
                            

But sometimes you want ready-to-render HTML in your api endpoint:

                                // pre-rendered html response instead of json
return view('user.index', ['users' => $users]);
                            

The frontend just injects that HTML fragment into the DOM. No need for JSON to HTML rendering.

Why DTOs and AQC Over GraphQL for Laravel

  • No extra layer: You stay inside Laravel. No resolvers, no schema language, no GraphQL servers.
  • Familiar workflow: API controllers, repositories, models—the same workflow Laravel devs already live in.
  • Strict contracts: DTOs ensure only the fields you want ever leave your backend. That’s your GraphQL-like precision.
  • Flexible responses: JSON for APIs or Blade-rendered HTML fragments for fast DOM updates. GraphQL doesn’t give you this choice.
  • Developer focus: The backend decides what’s appropriate. The frontend doesn’t dictate your database access strategy.

DTO + AQC = GraphQL Precision, Laravel Simplicity

GraphQL’s promise is “fetch only what you need.” With DTOs and AQC, you can deliver that promise without dragging another layer into your backend.

  • DTOs define the contract.
  • AQC makes queries explicit and efficient.
  • Responses can be JSON or HTML, depending on what your frontend actually needs.

So, skip the extra learning curve. Laravel doesn’t need GraphQL—you can already do it all with DTOs and AQC.

Final Thoughts

GraphQL is not the only way to fetch the required data. This is an alternate approach for developers who don't want to learn a new layer.


If you found this post helpful, consider supporting my work — it means a lot.

 Raheel Shan | Support my work
Raheel Shan | Support my work
Support my work
raheelshan.com
Comments


Comment created and will be displayed once approved.