As far as comparisons go, this might seem a bit weird. While the first two are (were) traditional competing "standards", the other two are a bit different in scope.
What unites them is the general idea of transferring control of data manipulation. Each of these allow an external entity to access your data within some boundaries.
The "Simple Object Access Protocol" (though largely this meaning has been rightly forgotten since it's anything but simple) is the oldest system for data access as well as the bane of my university years.
It communicates using XML content over HTTP (mostly, but the transport isn't a part of the standard - so it could be used over anything) for the specific goal of invoking procedures exposed by a service.
This requires the service to define the available procedures, its parameters and types, content format, responses, etc in a WSDL that defines the service while it also needs a bunch of other XML-offshoots (XSD, etc) to define custom types and objects.
I bet you're seeing where the simplicity has gone to die.
SOAP allows for code generation based on XML, XML is (was) a popular and ubiquitous standard with plenty of flexibility (eg: namespacing). As text-based standard it's easy to debug and has no restrictions in transport. Since the procedures are described in a WSDL, the file acts as a binding contract with respect to messages and types.
XML is very verbose and message size tends to grow exponentially. By necessity, SOAP is one way (as in a client can't be also a server and the other way around - even if both applications can act as web servers). Support for code generation in modern languages is lackluster (your best bets are Java, Python and C#, while SOAP with the likes of Go/Rust/PHP/Elixir are exercises in frustration).
All in all, SOAP is all but dead in favour of more flexible and efficient communication methods (though my nightmares have returned after a few projects around insurance and banking).
The REpresentational State Transfer is probably the thing most people think about when someone says "web API". In fact, it's so popular that it's the thing people think about when someone says "API", nevermind the web (or HTTP) part.
As a standard, it's surprisingly lightweight as it only specifies the use of HTTP and HTTP concepts (verbs, codes, states, etc) in order to access remote entities found at various URIs. This has led to the existence of a lot of best-practice scenarios around REST, that deal with the unsaid: how to define and organise URIs, the boundaries of the data manipulated at a given URI, how to encapsulate data, how and what to return, etc.
Another important property of REST services is statelessness: no context should be preserved between requests, each is treated in isolation and must have a finality of its own.
While JSON is the most common content type for REST data, XML is not unheard of and HTML itself is available.
Building on the existing standards of HTTP comes with both flexibility and constraints. Most of transport concerns are handled by existing standards and evolve with it. Verbs are associated with operations (making simple a mapping of CRUD operations for example), entities lend name and meaning to URIs. Simple conventions make interacting with REST APIs fairly straightforward.
JSON makes REST particularly straightforward to use for frontend services where JS is ubiquitous so that the translation of data to objects is instant.
Also, JSON is quite human readable and easy to debug.
In practice, REST services are rarely as forthcoming as intended. There are endless debates about which verbs to use when and whether exposed entities are the same as data modelled in the data layer (hint: don't do that, don't leak this kind of details, use DTOs for the win).
The flexibility often translates into implementation chaos and on top of that there's no binding contract on the structure use for messages.
A particular pet peeve of mine is when people return HTTP 200 response in any case and instead encode an error status in a response body. That's what SOAP people used to do!
gRPC Remote Procedure Call is a recursive acronym for a method defined around all the advantages of HTTP/2 doubled on by the fact it uses a binary format for transfer (compiled protocol buffers) which is extremely efficient.
In the likes of SOAP, gRPC's goal is to enable procedure calls rather than data interactions, making it somewhat SOAP-like in concept.
It's Google's brainchild.
Very efficient. Takes advantage of all HTTP/2 has to offer in that you can send things synchronously (waiting for a response), you can stream, you can multiplex, all over the same connection. Add the fact that binary messages are damn small, you got a sweet deal for those times when performance is critical.
Code generation makes things a breeze to setup and HTTP/2 needs TLS so you're somewhat forced into a minimum of security.
Hard to debug, as messages are binary and not human readable. While it's supported across languages, some have limitations (eg: you can't have PHP gRPC servers, only clients) and due to the nature of HTTP/2 there's no support (currently) directly in frontend applications.
Since you must (and should) have TLS everywhere, you'll need to factor that into setup requirements (whether it's acquiring certificates for internal use from a known CA or creating your own internal CA to issue certificates, but you'll need to add it as trusted across your infrastructure/containers/etc).
GraphQL sits somewhere between REST and gRPC. It aims to make requesting data easy through its own query language that gives control to the client.
It's Facebook's brainchild.
Its reliance on JSON makes it somewhat REST-like but with the added benefit that the adaptive queries means you can request for the data you want as you want it => no more multiple requests to get everything you need.
It has schema validation and typing so in that sense it's somewhat close to how gRPC defines things.
All the flexibility takes a toll on the ability to cache. Just about none of the GraphQL requests are similar to warrant caching.
Despite the name, it's not quite a graph interface. You can't take all the ancestors of a parent entity, for example.
I've been plagued by SOAP and I think I'm not the only one. I want to warn people about the abomination that still roams the dark underbelly of web development.
I've been a dedicated REST developer and took me years to develop a methodical approach to API design. It's ok, but I really think there should be a better way. Despite all the good practices and so on, the vast majority of projects end in a sort of manageable chaos and the lack of verifiable contracts on messaging is a killer.
I've tried GraphQL and I see its value. It's awesome for frontend development (you can see its Facebook origins make it a friend to React) but it's a one-way street. I can't imagine making microservices to talk among themselves in GraphQL. It's more structured than REST but the benefits are pretty much client-side.
GRPC is growing on me. Depending on your tooling, there may be some overhead. PHP setup with building
grpc_php_plugin is painful and heavily dependent on OS (sorry Windows users, you may need to get your hands dirty with WSL2 - but please let me know if I'm wrong though the last time I checked the prebuilt plugin was deprecated). Going at GRPC via Go is amazing (yeah ... coming from Google after all) but with Rust/DotNet/Elixir isn't bad either.
I can only hope to get my GRPC habits up and running the way I did with REST.