Challenges with GraphQL
Disclaimer: this isn't a fair an balanced examination. This is just the rough edges. There are plenty of good parts as well.
Writes
GraphQL has support for writing to the server, called mutations, but it's rudimentary compared to what is available in a web/REST based system. HTTP has three types of write mechanisms, POST, PUT, and PATCH. Each of these has particular guarantees that provide benefits in different cases.
PUT requests are idempotent which gives clients the power to retry a request without worrying about how the second request might break the state of the system. Idempotent PUTs also allow for more efficient caching. An HTTP Cache layer that receives a PUT request can cache that request and use it to respond to subsequent GET requests without having to make a request to the origin server. This is called write-through caching.
Concurrency
PUT and PATCH also have concurrency control mechanisms. A request can contain an ETag that identifies that client's previously known state of the resource. If the resource has changed on the server since the request is made, the server can reject the request. This protects against two clients overwriting each other's updates. GraphQL mutations have no concurrency protection mechanisms.
Caching
GraphQL is notoriously bad at caching, which has implications for scalability. Since every query can be different, the benefits of caching query results is reduced. Also, GraphQL can't make use of HTTP Cache layers because it tunnels requests through POST. So, caching needs to be done manually on the client and server.
Logging / Monitoring
GraphQL also has difficulty making use of existing logging and monitoring tools. With REST, tools can capture what URL was hit, the HTTP method used, the response status code, etc. With GraphQL, you just see a bunch of POSTs to the same endpoint with no clear indication of whether each call was successful or not.
Security
When using GraphQL for a public API, you don't have control over what queries API users are executing and therefore can't optimize the system for poor performing queries like you would for a relational database in a closed system. People can conceivably construct queries in ways you haven't thought of that put an unexpected amount of strain on the system.
Recommendations
Given these challenges, it would be reasonable to consider GraphQL if your API,
- is internal-only (you control all the clients) (see: challenges with security)
- is mostly reads of graph-like data (see: challenges with writing)
- doesn't have the possibility of different clients trying to modify the same resources (see: challenges with concurrency)
- can't be cached effectively (see: challenges with caching)
Generally, I would say you probably want to start with a REST API and if there are parts of you application that fit GraphQL really well, you can always add GraphQL by having your resolvers call the REST API.