If I'm building a backend in JS/TS, GraphQL is getting to be just about a no brainer. The tooling for GraphQL, such as Apollo's server products, in JS is fantastic, and can be often highly productive.
On the flipside, if I'm working on .NET (Core) backend, the GraphQL tools are rather lacking still, but a lot of the REST tools are very well refined and capable.
There's a wide spectrum in between of course, too, such as Python which is growing pretty good tools for GraphQL (Graphene I've heard is really good to work with), but also has great REST tools.
Similarly client stack matters too. Again, JS clients for GraphQL are mostly ahead of most other languages. React is great with GraphQL, but some of the other frameworks don't fit the GraphQL model quite as well.
Beyond that, other judgement calls are useful such as development team familiarity and project needs.
The whole idea of GraphQL was actually to make complex queries easier and more performant (it's a bit like SQL for HTTP).
My personal conclusion (ymmv) is to build REST APIs whenever the API should be exposed to external developers or to be integrated with 3rd party software. On the other side I like GraphQL very much, when the only (main) purpose of the API is to build the backend for a SPA.
How about REST's OData?
GraphQL has better mitigations for sidelining problematic queries (query names, shapes/patterns analysis tools), and doesn't start with the kitchen sink like OData did. Instead, in GraphQL you generally have to opt-in to more complex query operations, at which point you can also lock them down to explicit clients/whitelisted query names/etc while doing so.
[1] NuGet's feeds being the big obvious one to mind where v1 API supported nearly the full OData query set and every version since has dropped increasingly more OData functionality for bespoke REST endpoints.
Isn't that an implementation issue? Nevertheless IIRC some OData APIs do implement anti-DoS features such as rate limiting.
I think some of the "keep it simple stupid" principle of what REST does well that SOAP failed to do, applies between OData and GraphQL. OData did a really good job of very easily connect (via LINQ for many users) "SQL" over the wire, which is great when that's what you need. It's just that "SQL" was never optimized for that and the solutions like rate limiting have to be bolted in on top of the design, and often start to break the original appeal "'SQL' over the wire" as the subset of "SQL" that reliably works shrinks (and therewith shrinks the ability to do some of the ad hoc queries that were the draw in the first place).
GraphQL doesn't entirely escape the orbit of query language as wire protocol issues, of course, but it has the benefit of having seen the issues in OData to learn from, and from what I've seen mitigates a lot of the worst cases in its opt-in approach and the fact that it looks nothing like "SQL" so it immediately doesn't inherit the assumptions of SQL and what sort of queries may or may not be immediately available.
Using GraphQL to create, fetch and write individual objects in a typical CRUD app, without complex object relationships, adds a good bit of unnecessary complexity. Luckily, this is where REST shines.
If/when the domain model evolves, building a parallel GraphQL API for complex (read-only) queries will help solve querying/filtering/complex object fetching better than REST can.
GraphQL for writes/updates doesn't seem worth it for general use cases, but can be appropriate if it matches your domain model.
We are getting a fair amount of cacheing and state management for "free" by using Apollo with GraphQL.
You can then spend most of your time on making sure you have a good understanding of the domain and then design the API to match that understanding.
Rails because you asked what _I_ would reach for :) Once you figure out your domain model you could just generate the REST API.
Rails has GraphQL gems so you could go down that path in the future if desired. Like anything though, GraphQL isn't a silver bullet. If you have a complex data model, that can be exposed and people unfamiliar with the data model will see performance issues.
When I hear API, I usually infer that it means other people will be calling it. The above answers are in context of that. If you really mean a back-end to your web app that nobody external will be calling, ever, then it really doesn't matter what tech stack or methodology you use. In that case, there are other factors like time to deliver and if there is a team building it, then it helps to use a common methodology (e.g. REST).
I do appreciate how productive GraphQL and frontend frameworks like Gatsby can make developers. Having seen it first hand in hackathons ;)
e.g. if you have particularly demanding performance requirements around latency and speed of decoding messages, then perhaps you cannot use either GraphQL or REST.