# After REST: Trying Out GraphQL

> While solving a real over-fetching problem with GraphQL, I weigh where REST is still sufficient and where GraphQL actually makes sense.

- Published: 2021-10-03
- Category: Web Development
- Tags: API, GraphQL
- Reading time: 3 min read
- Source: https://www.muhammetsafak.com.tr/en/blog/trying-graphql-after-rest/
- Language: en-US
- Author: Muhammet Şafak

---
GraphQL had been on my radar for years, but I kept putting it off with the "[REST already works](/en/blog/fundamentals-of-restful-api-design/)" feeling. Mid-year on one project I ran into a concrete problem, and solving that problem actually required giving GraphQL a real try. Now I'm writing up both what I found and where REST still holds up better.

## The concrete problem: too many requests for a single mobile screen

An app screen displayed: a user profile, their last three orders, and the product names within each order. With REST we were making three separate endpoint calls to assemble this:

1. `GET /api/user/42` → profile
2. `GET /api/users/42/orders?limit=3` → orders
3. `GET /api/orders/{id}/items` for each order (at least three more requests)

Five to six HTTP requests in total. Each one waits for a response, each one involves JSON parsing. Network latency was slowing down the screen's load time noticeably.

On top of that, every endpoint was bringing back more data than we needed. The order list included order date, payment method, shipping address — none of which appeared on the screen. We had to fetch, transfer, and parse all that extra data anyway.

REST has standard solutions for these problems: [eager loading](/en/blog/eloquent-relationships-hasmany-belongsto-and-eager-loading/) via an `include` parameter, custom endpoints, API Gateway aggregation... But all of those put extra burden on the server side, and every new screen requirement calls for a new endpoint or a new parameter arrangement.

## What GraphQL is

GraphQL (Graph Query Language) is an API query language developed by Facebook and open-sourced in 2015. It is not a protocol like REST; it is a query mechanism where the client specifies exactly what it wants.

A single endpoint (`/graphql`) returns different data depending on the query the client sends:

```graphql
query UserDashboard($userId: ID!) {
  user(id: $userId) {
    name
    email
    orders(limit: 3) {
      id
      status
      items {
        productName
      }
    }
  }
}
```

This single query fetches in one round-trip what five or six REST requests used to cover. The client declares exactly which fields it needs; the server returns only those fields.

## Setting up GraphQL in Laravel with PHP

The `rebing/graphql-laravel` package makes standing up a GraphQL server in Laravel relatively straightforward:

```bash
composer require rebing/graphql-laravel
```

Type definition:

```php
<?php

use Rebing\GraphQL\Support\Type as GraphQLType;
use GraphQL\Type\Definition\Type;

class UserType extends GraphQLType
{
    protected $attributes = [
        'name'  => 'User',
        'model' => \App\Models\User::class,
    ];

    public function fields(): array
    {
        return [
            'id'    => ['type' => Type::nonNull(Type::id())],
            'name'  => ['type' => Type::nonNull(Type::string())],
            'email' => ['type' => Type::nonNull(Type::string())],
        ];
    }
}
```

Query definition:

```php
<?php

use Rebing\GraphQL\Support\Query;
use GraphQL\Type\Definition\Type;
use GraphQL\Type\Definition\ResolveInfo;

class UserQuery extends Query
{
    protected $attributes = ['name' => 'user'];

    public function type(): Type
    {
        return GraphQL::type('User');
    }

    public function args(): array
    {
        return [
            'id' => ['type' => Type::nonNull(Type::id())],
        ];
    }

    public function resolve($root, array $args, $context, ResolveInfo $resolveInfo)
    {
        return User::findOrFail($args['id']);
    }
}
```

## The N+1 problem: GraphQL's classic trap

The moment you start using GraphQL, the N+1 problem hits you in the face. When you request a user's orders and then the items within each order, the resolver can open a separate database query for every single order.

In the PHP ecosystem there are **dataloader** libraries to address this. Tools like `overblog/dataloader-php` batch the requests. What you solved with `with()` in REST/Eloquent needs more deliberate management in GraphQL.

## An honest comparison

**When GraphQL makes sense:**
- Multiple clients (web, mobile, third-party) share the same API and each has different data needs.
- Over-fetching and under-fetching are creating a real performance problem.
- You want to open new queries for rapidly changing UI requirements without touching the backend.

**Where REST still holds up better:**
- There is a single client and the data requirements are relatively predictable.
- The workload is predominantly simple CRUD operations.
- Caching is critical: REST endpoints cache easily with HTTP caching, while GraphQL POST requests make that considerably more painful.
- The team is not yet mature enough to invest in the GraphQL toolchain.

At the end of this experiment I decided to reach for GraphQL specifically in the places where over-fetching is a genuine problem, rather than adopting it everywhere. REST and GraphQL can coexist quite comfortably within the same service.
