GraphQL and REST APIs: An Introduction

Published on: February 11, 2025

GraphQL and REST are popular architectures for building web APIs (Application Programming Interfaces) and share the goal of enabling web-based communication between clients and servers for data access and manipulation. They both leverage common web technologies like HTTP and JSON. Both are generally designed to be stateless to enhance scalability and reliability. Both are supported by mature ecosystems with comprehensive documentation and tooling. The differences lie in how they achieve their goals, primarily in their approach to data fetching, endpoint structure, and the level of flexibility given to clients.

REST APIs

Let's first examine REST (Representational State Transfer) APIs. A REST API:

  • Is focused on resources, like “users,” “products,” and “orders.” Each resource is identified by a unique URL (endpoint). Typically, each endpoint returns a fixed and predefined set of data for that resource. Often, clients either receive more data than was needed from an endpoint (over-fetching) or must make requests to multiple endpoints to get all data needed (under-fetching).
  • Uses standard HTTP methods (POST, GET, PUT, and DELETE) to create, retrieve, update, or delete (CRUD) resources.
  • Uses standard HTTP status codes, such as 200 for success and 400 for a bad request.
  • Can achieve real-time updates though not part of the core REST archictecture and often layered on top. It often requires relying on client-driven techniques like frequent polling (repeatedly asking for updates) or server-push technologies such as Server-Sent Events (SSE) or WebSockets (for more persistent connections).
  • Is often versioned when changes are made or new features are added, to maintain backward compatibility. Endpoints usually include the version (e.g., /v1/users, /v2/users).
  • Is often considered simpler to get started with for basic APIs, especially CRUD operations on resources.

GraphQL APIs:

GraphQL is a query language for your API and a runtime to fulfill queries with a client's data. A GraphQL API:

  • Is defined by a strong schema that details all the data types, fields, and relationships available in the API. This schema is introspectable, meaning clients and tools can query the schema to learn what data is available.
  • Typically has a single endpoint, meaning all GraphQL queries and mutations are sent to the same URL, usually /graphql. Clients send all their queries to this endpoint.
  • Returns only the data requested by the client. The client specifies exactly what data and fields will be returned in queries.
  • Have three main operations built in: queries to fetch data (like GET), mutations to modify data (like POST, PUT, and DELETE), and subscriptions to push real-time data updates to clients over persistent connections.
  • Primarily uses HTTP status code 200 for all responses and embeds error information within the response body. There are circumstances when it will respond with standard HTTP-level errors when problems in the request exist before GraphQL processing, such as 500 for server errors, 400 for a malformed request, or 401 for authentication or authorization failures.
  • Minimizes the need for versioning. Fields can be deprecated in the schema, and new fields can be added, without breaking existing clients.
  • Has a steeper learning curve due to its schema, type system, and query language. Server-side implementation can also be more complex initially to set up resolvers and data fetching efficiently.

Example client-server exchanges

Let's take a GraphQL API and a REST API that each communicates movie data, such as movie title, overview, director, and cast. The following sections show sample exchanges between the client and API.

GraphQL API

JSON query from the client requesting specific data (director and cast) on a particular movie:

query GetMovieDetails {
  movie(id: 123) {
    director
    cast {
      actor {
        name
      }
      character
    }
  }
}

Server JSON response, providing only the data requested:

{
  "data": {
    "movie": {
      "director": "Steve Doe",
      "cast": [
        {
          "actor": {
            "name": "Jane Doe"
          },
          "character": "Character A" 
        },
        {
          "actor": {
            "name": "John Doe"
          },
          "character": "Character B"
        }
      ]
    }
  }
}

REST API

Let's assume our base URL is https://restapi.example.com/api. To get the same data (director and cast) in the REST API, the client first makes a GET request for the general movie details, which includes the director: https://restapi.example.com/api/movies/123.

The server JSON response includes all the general movie details:

{
  "movieId": 123,
  "title": "Example Movie",
  "overview": "...",
  "homepage": "https://example-movie.com",
  "director": "Steve Doe",  
  "budget": 50000000,
  "revenue": 150000000,
  "releaseDate": "2024-01-15",
  // ... other movie details ...
}

Then, the client makes a second GET request for the cast: https://restapi.example.com/api/movies/123/cast.

The server JSON response:

[
  {
    "actorName": "Jane Doe",
    "characterName": "Character A"
  },
  {
    "actorName": "John Doe",
    "characterName": "Character B"
  },
]

More on GraphQL introspection

This section shows an abbreviated version of the movie type in the schema from the movies-related GraphQL API (written in PHP), the client's introspection query, and the server's response.

Documentation is built into the schema. Note that 'homepage' has a deprecationReason option to indicate it is deprecated. This information is discoverable through introspection:

    $movieType = new ObjectType([
        'name' => 'Movie',
        'description' => 'Represents a movie in the database.', 
        'fields' => [
            'id' => [
                'type' => Type::nonNull(Type::id()),
                'description' => 'The unique identifier of the movie.',
            ],
            'title' => [
                'type' => Type::string(),
                'description' => 'The title of the movie.', 
            ],            
            'homepage' => [
                'type' => Type::string(),
                'description' => 'The official homepage URL of the movie.', 
                'deprecationReason' => 'Use `officialWebsite` field instead. This field will be removed in the future.', 
            ],
            'cast' => [
                'type' => Type::listOf(new ObjectType([
                    'name' => 'CastMember',
                    'description' => 'Represents a cast member in a movie.', 
                    'fields' => [
                        'actor' => [
                            'type' => new ObjectType([
                                'name' => 'Actor',
                                'description' => 'Represents an actor.', 
                                'fields' => [
                                    'id' => [
                                        'type' => Type::nonNull(Type::id()),
                                        'description' => 'The unique identifier of the actor.', 
                                    ],
                                    'name' => [
                                        'type' => Type::string(),
                                        'description' => 'The name of the actor.', 
                                    ],
                                ],
                            ]),
                            'description' => 'Information about the actor.',
                        ],
                        'character' => [
                            'type' => Type::string(),
                            'description' => 'The name of the character played by the actor.', 
                        ],
                    ],
                ])),
                'description' => 'The list of cast members in the movie.', 
            ],
        ],
    ]);

 

Client JSON Introspection query on the movie type:

query IntrospectionQueryForMovieType {
  __type(name: "Movie") { # Get the "Movie" type
    name
    description # Get the description of the Movie type
    fields {     # Get the fields of the Movie type
      name
      description # Get the description of each field
      isDeprecated # Is the field deprecated?
      deprecationReason # If deprecated, why?
      type {        # Get the type of each field
        name
        kind
        ofType {   # If it's a List or Non-Null type, get the underlying type
          name
          kind
        }
      }
    }
  }
}

 

GraphQL JSON response, providing the full details:

{
  "data": {
    "__type": {
      "name": "Movie",
      "description": "Represents a movie in the database.", 
      "fields": [
        {
          "name": "id",
          "description": "The unique identifier of the movie.", 
          "isDeprecated": false,
          "deprecationReason": null,
          "type": {
            "name": "ID",
            "kind": "NON_NULL",
            "ofType": {
              "name": "ID",
              "kind": "SCALAR"
            }
          }
        },
        {
          "name": "title",
          "description": "The title of the movie.", 
          "isDeprecated": false,
          "deprecationReason": null,
          "type": {
            "name": "String",
            "kind": "SCALAR",
            "ofType": null
          }
        },     
        {
          "name": "homepage",
          "description": "The official homepage URL of the movie.", 
          "isDeprecated": true, // 
          "deprecationReason": "Use `officialWebsite` field instead. This field will be removed in the future.", // 
          "type": {
            "name": "String",
            "kind": "SCALAR",
            "ofType": null
          }
        },
        {
          "name": "cast",
          "description": "The list of cast members in the movie.", 
          "isDeprecated": false,
          "deprecationReason": null,
          "type": {
            "name": "CastMember",
            "kind": "LIST",
            "ofType": {
              "name": "CastMember",
              "kind": "OBJECT"
            }
          }
        },
      ]
    }
  }
}

Conclusion

Choosing Between GraphQL and REST depends on your specific needs and project context.

  • For simpler APIs, especially CRUD operations, and public APIs emphasizing discoverability, REST can be a straightforward and well-established choice.
  • For complex applications, demanding frontends, real-time features, and situations where client-side data fetching control and efficiency are paramount, GraphQL offers significant advantages.

Therefore, the best choice between REST and GraphQL, or even the strategic use of both, depends on carefully evaluating the specific requirements, complexity, and priorities of each service or application within an organization's overall architecture.

For more information about GraphQL and REST, check out the resources below: