Serdar Yegulalp
Senior Writer

What is GraphQL? Better APIs by design

feature
Jul 24, 20248 mins
APIsOpen SourceWeb Development

Learn how the open standard for querying data is different from REST and Swagger, and how you can use it to build more flexible and responsive APIs.

Paper cranes, change, evolution. A colorful paper crane becomes a paper airplane in flight.
Credit: Lightspring / Shutterstock

When most software developers think of web APIs, we think of REST, or Representational State Transfer. With REST, you send a request to a request-specific URL and receive the results in whatever format makes sense for the application.

Metaโ€™s web API system, GraphQL, is a different kind of API. With GraphQL, developers use a strongly typed query language to define both requests and responses, allowing an application to specify exactly what data it needs from an API. Thus GraphQL is meant as a more efficient, structured, and systematic alternative to REST.

In this article, you will learn how GraphQL differs from REST, how those differences impact API design, and why GraphQL often makes a better choice than REST for fetching data from a server.

GraphQL vs. REST

With REST, you typically submit your request by way of a specially crafted URL, with each variety of request sent to a different endpointโ€”for instance, /movie/2120 vs. /director/5130.

With GraphQL, you submit a declarative request in a JSON-like query for the data youโ€™re seeking, and all requests go to the same endpoint. The schema used for the request determines what data youโ€™ll get back.ย Itโ€™s a standardized, self-describing way to ask for the specific data you need, and only the data you need. Using different schemas for different types of requests, rather than a different endpoint URL format, makes for a much more flexible query mechanism.

Although many REST APIs also conform to a common specification, Swagger, thereโ€™s no rule that says a REST API has to be generated by Swagger. GraphQL provides a formal definition for the API by default. In this respect, GraphQL is a little like SQL. With an SQL-powered data source, you connect to one common endpoint for all your data requests, and the formatting of the request determines what records youโ€™ll get back. And while there are many different implementations of SQL, the syntax of SQL queries remain highly consistent across those implementations.

GraphQL queries

So, weโ€™ve already said that GraphQL uses a schema, or a data definition, to describe how the data to be retrieved is organized in the query and the response. Anyone who has worked with an ORM (object-relational mapper) should find GraphQLโ€™s data schema definitions familiar. Hereโ€™s an example:


type Movie {
    id: ID
    title: String
    released: Date
    director: Director
}

type Director {
    id: ID
    name: String
    movies: [Movie]
}

Youโ€™ll notice that each element in the schema has a type definition. GraphQL has its own type system for queries, which is used to validate the incoming schema and to return data in a format consistent with the definition.

Queries submitted to GraphQL are similarly defined by a schema:

type Query {
    movie_title(title: String!): Movie
    director(id: ID): Director
}

Here we have a query that takes in up to two parameters, movie_title (by way of its title), and director (also by way of an ID.)

The ! next to a type indicates that this is a mandatory element in the query. In other words, you must query for a movie by title, with the director being optional (as a way to narrow the query).

Mandatory elements can also be used in a data schema. Hereโ€™s one possible way to format a query that searches for movies by their ID only:


query GetMovieByID ($id: ID!) {
    movie(id: $id) {
        name
    }
}

This query uses a separately defined variable ($id), which is required, to look up a movie by its ID number and return its name. Note that GraphQL queries can return related objects and their fields, not just individual fields. It does this by nesting arrays of items inside fields.

Variables for queries are passed along in a separate section of the query, using a format like this:


{
    โ€œidโ€: 23
}

GraphQL types

GraphQLโ€™s query type system specifies many common scalar types, like strings and integers. Most queries will revolve around those. But the type systemย also includes several advanced types for more sophisticated queries:

  • The interface type can be used to create an abstract type with a pre-defined set of fields, which other types can implement and re-use.
  • The union type allows different kinds of results to be returned across multiple types from a single kind of query.
  • input types can be used to pass whole objects of the above kind as parameters in a query, provided those objects are created out of common scalar types that can be validated.

If youโ€™re working with interface or union objects, youโ€™ll need to use inline fragments and directives to return data based on the conditions those object types can specify.

Another kind of type that can be returned in a query is the edge type, returned in an optional edges field. Edges contain nodesโ€”data recordsโ€”and cursors, which are encoded strings that provide contextual information about how to paginate backwards or forwards from that object.


{
    movie {
        name
        actors (first:5) {
            edges {
                cursor
                node {
                    name
                }
            }
        }
    }
}

In this example, a movie node would return the name of the movie and its actors. For each actor in the movie, weโ€™d receive a node containing the actorโ€™s name and a cursor that allows browsing the actorโ€™s โ€œneighbors.โ€

Pagination with GraphQL

A common scenario when working with any data source is to request data in pages by way of a cursor. GraphQL provides a number of ways to do pagination.

When you request records, you can specify not only how many records to request and the starting offset, but how toย request successive pages. The example code in the previous section returns only the first five actors associated with a given movieโ€”as indicated by the first:5 parameter in parentheses.

The first: clause after actors can be followed by other keywords that describe how to fetch the succeeding items. offset: can be used for simple offsets, but the offset might be thrown off when data is added or deleted.

For the most robust pagination, youโ€™ll want to use a cursor that can be delivered along with the object youโ€™re requesting, by using the edge type described above. This allows you to create pagination mechanisms that are not disrupted when data is inserted or deleted between paginationsโ€”for instance, by using the objectโ€™s unique IDย (or any other attribute as long as itโ€™s unique) as a starting key index for other calculations.

Changing data with GraphQL mutations

With a REST API, you make changes to the server-side data by submitting requests that use POST, PATCH, and other HTTP verbs. With GraphQL, you use a specific query schema to make changes, a mutation queryโ€”again, in much the same way SQL uses UPDATE or DELETE queries.

To make changes to the data, you submit a GraphQL query using a schema called a mutation schema:

mutation CreateMovie ($title: String!, $released: Date!) {
    createMovie (title: $title, released: $released){
        id
        title
        released
    }
}

[submitted data]

{
    โ€œtitleโ€: โ€œSeven Samuraiโ€
    โ€œreleasedโ€: โ€œ1950โ€
}

All queries, including mutation queries, canย return data.ย Here, the list of fields in the curly braces after createMovie specifies what we want to see returned from the server after a new record is created with this request. The value for theย id field, in this case, would be created by the database; the values for the other fields are submitted in the query.

Another thing to keep in mind is that the queries and data types used to return data are by design different from those used to request data. Mutation queries need to validate incoming data, so the types used for those queries are meant to serve that function. Likewise, the fields used in returned query objects are for display, not validation. If you take in a GraphQL object as returned from a query, it might have fields with circular references or other issues that make it unusable as a query parameter.

Why use GraphQL?

A key reason to choose GraphQL over REST is the explicit, declarative nature of GraphQL queries. Having a formal definition for how queries and returned data should look has advantages aside from being consistent across APIs and implementations.

As API evangelist Phil Sturgeon noted in his examination of GraphQL vs. REST, the field structure of GraphQL makes it easier to apply more granular versioning to queries, since specific fields can be deprecated or rolled in over time as opposed to versioning the entire API. Itโ€™s still possible to take the wholesale versioning approach with GraphQL; the point is that you arenโ€™t forced to do so when rolling out changes.

Sashko Stubailo, engineering manager at Apollo GraphQL, maker of open source tools for GraphQL APIs, notesย another advantage of the GraphQL approach: Itโ€™s self-documenting. โ€œEvery possible query, object, and field comes with a name, description, and type information that can be queried from the server in a standard way,โ€ Stubailo writes.

The self-documenting nature of GraphQL also provides a sort of introspection, meaningย you can use queries to return information about themselves. This way, software that works with GraphQL queries doesnโ€™t have to be hard-wired to work with any particular field set; it can infer the fields automatically.

The fact that GraphQL is newer and REST/Swagger are older should not, by itself, be a reason to favor GraphQL. As Arnaud Lauret, author of The Design of Everyday APIs said in a discussion of the two standards: โ€œA GraphQL API, just like a REST one, must be created with a purpose and designed from an outside-in perpective and not an inside-out one.โ€

Serdar Yegulalp

Serdar Yegulalp is a senior writer at InfoWorld. A veteran technology journalist, Serdar has been writing about computers, operating systems, databases, programming, and other information technology topics for 30 years. Before joining InfoWorld in 2013, Serdar wrote for Windows Magazine, InformationWeek, Byte, and a slew of other publications. At InfoWorld, Serdar has covered software development, devops, containerization, machine learning, and artificial intelligence, winning several B2B journalism awards including a 2024 Neal Award and a 2025 Azbee Award for best instructional content and best how-to article, respectively. He currently focuses on software development tools and technologies and major programming languages including Python, Rust, Go, Zig, and Wasm. Tune into his weekly Dev with Serdar videos for programming tips and techniques and close looks at programming libraries and tools.

More from this author