DEV Community

Margaret Apiyo
Margaret Apiyo

Posted on

A Practical Guide to API Types: REST, GraphQL, gRPC, SOAP, and WebSocket.

A Practical Guide to API Types and When to Actually Use Each One

Every job posting says "experience with APIs," but that phrase covers a lot of ground. REST, GraphQL, gRPC, SOAP, WebSocket, they're all "APIs," yet they solve different problems and make different trade-offs. I've shipped code against all five, and picking the wrong one for a project is the kind of mistake you don't feel until six months later when you're stuck maintaining it.

This post covers what an API actually is, then breaks down the major styles you'll run into on the job and how each one works, what a request/response actually looks like, and when you'd reach for it over the others.

What Is an API

An API (Application Programming Interface) is a interface that allows one piece of software to communicate to another without either side needing to know how the other one is implemented internally. You send a request in an conventional format, you get a response back in an conventional format, done.

The differences between API styles come down to a handful of design decisions:

  • What does a request/response actually look like on the wire?
  • Is communication one-shot (request → response) or ongoing (a persistent connection)?
  • How strict is the interface between client and server?
  • How is data structured,documents,remote procedure calls, or something else?

Let's go through the major styles.

1. REST (Representational State Transfer)

REST is quite common API that most people interact with either knowingly or unknowingly. It's not a protocol like HTTP or TCP,it's an architectural style, a set of conventions for using HTTP the way it was originally intended.

How it works:

  • Everything is a resource, addressed by a URL (/users/42, /orders/107/items)
  • You interact with resources using standard HTTP methods: GET (read), POST (create), PUT/PATCH (update), DELETE (remove)
  • Responses are usually JSON (though XML is technically fine too)
  • REST is meant to be stateless ,each request carries everything the server needs; the server doesn't remember previous requests

Example:

GET /api/users/42 HTTP/1.1
Host: example.com

Response:
{
  "id": 42,
  "name": "Apiyo Okafor",
  "email": "apiyo@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Creating a resource looks similar, just with a different verb and a body:

POST /api/users HTTP/1.1
Host: example.com
Content-Type: application/json

{
  "name": "Apiyo Okafor",
  "email": "apiyo@example.com"
}
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • Simple, widely understood, huge tooling ecosystem
  • Plays nicely with HTTP caching, load balancers, and CDNs
  • Easy to debug with just a browser or curl

Weaknesses:

  • Over-fetching / under-fetching: GET /users/42 might return fields you don't need, and you might need three more requests to get related data
  • No built-in strict schema,contracts often live only in documentation (or Swagger/OpenAPI specs)
  • Versioning (/v1/, /v2/) can get messy over time

Best for: public APIs, CRUD-heavy applications, anything where cacheability and simplicity matter more than flexibility.

2. GraphQL

GraphQL came out of Facebook, built specifically to fix REST's over-fetching and under-fetching problem. Instead of many fixed endpoints, you get a single endpoint, and the client describes exactly the shape of data it wants back.

How it works:

  • One endpoint, typically POST /graphql
  • The client sends a query describing the fields it needs, and the server returns exactly that, no more, no less
  • A strongly typed schema defines every possible query, mutation, and type up front

Example:

query {
  user(id: 42) {
    name
    orders(last: 3) {
      total
      date
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

This single request replaces what might be two or three REST calls, and it returns only the fields listed. Writes work through mutations, which look similar:

mutation {
  updateUser(id: 42, input: { name: "Apiyo N. Okafor" }) {
    id
    name
  }
}
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • Client controls exactly what data comes back,no over-fetching
  • One request can pull nested/related data in one round trip
  • Strong typing means better tooling (autocomplete, validation, introspection)

Weaknesses:

  • Caching is harder since there's no URL-per-resource for HTTP caches to key off
  • More complex to set up on the server (resolvers, schema design, query cost limits)
  • A single expensive/deeply nested query can strain the server if not guarded against

Best for: applications with complex, nested, or highly variable data needs, think mobile apps pulling data from many related sources, or frontends built by teams who don't want to wait on backend changes for every new field.

3. gRPC (Google Remote Procedure Call)

gRPC works differently from both. Instead of resources (REST) or queries (GraphQL), you're calling a function on another machine, and it feels almost like calling a local function.

How it works:

  • Built on HTTP/2, using Protocol Buffers (protobuf),a compact binary format instead of JSON
  • You define a .proto file describing services and message types, and gRPC generates client/server code in your language of choice
  • Supports streaming: client-streaming, server-streaming, and full bidirectional streaming, not just request/response

Example (.proto definition):

service UserService {
  rpc GetUser (UserRequest) returns (UserResponse);
}

message UserRequest {
  int32 id = 1;
}
Enter fullscreen mode Exit fullscreen mode

Once you generate code from that .proto file, calling it from Python looks like a normal function call:

response = user_service_stub.GetUser(UserRequest(id=42))
print(response.name)
Enter fullscreen mode Exit fullscreen mode

No manual JSON parsing, no guessing field names,the client is generated straight from the contract.

Strengths:

  • Very fast and bandwidth-efficient (binary format, HTTP/2 multiplexing)
  • Native streaming support,great for real-time or high-throughput data
  • Strict, generated contracts reduce integration bugs between services

Weaknesses:

  • Not human-readable on the wire and it is harder to debug without tooling
  • Poor browser support natively (needs gRPC-Web + a proxy)
  • Steeper learning curve, less friendly for public-facing APIs

Best for: internal microservice-to-microservice communication where speed and strict contracts matter more than human readability.

4. SOAP (Simple Object Access Protocol)

SOAP is the oldest style on this list, and you won't see it much in modern startups — but it's still running plenty of banking, insurance, and government systems, so it's worth knowing.

How it works:

  • Messages are strictly formatted XML, wrapped in an "envelope"
  • Comes with a rigid contract called WSDL (Web Services Description Language) that precisely defines every operation, input, and output
  • Includes built-in standards for security (WS-Security), transactions, and reliable messaging

Example:

<soap:Envelope>
  <soap:Body>
    <GetUser>
      <UserId>42</UserId>
    </GetUser>
  </soap:Body>
</soap:Envelope>
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • Extremely strict, well-defined contracts — great for formal enterprise integrations
  • Built-in support for security and transactional guarantees that heavily regulated industries need
  • Protocol-agnostic (can technically run over more than just HTTP)

Weaknesses:

  • Verbose XML payloads — slower and heavier than JSON or binary alternatives
  • Rigid and ceremonial; slower to develop against than REST or GraphQL
  • Considered legacy by most modern web/mobile developers

Best for: enterprise systems, financial services, healthcare, and other domains where formal contracts, security standards, and reliability guarantees are non-negotiable.

5. WebSocket

Everything above is request-then-response. WebSocket breaks that pattern — it opens a persistent, two-way connection between client and server, and either side can send a message whenever it wants, without waiting to be asked.

How it works:

  • Starts as an HTTP request that "upgrades" to a WebSocket connection
  • After the handshake, both client and server can push messages over the same open connection, with much lower overhead than repeated HTTP requests

Example (JavaScript client):

const socket = new WebSocket("wss://example.com/chat");

socket.onmessage = (event) => {
  console.log("New message:", event.data);
};

socket.send("Hello, server!");
Enter fullscreen mode Exit fullscreen mode

Strengths:

  • True real-time, bidirectional communication
  • Low overhead once the connection is open,no repeated HTTP headers per message
  • Ideal for anything that needs to "push" data without the client asking

Weaknesses:

  • Not resource-oriented — doesn't fit naturally with typical CRUD operations
  • Harder to scale/load-balance (connections are stateful and long-lived)
  • Overkill for anything that isn't genuinely real-time

Best for: chat apps, live dashboards, multiplayer games, collaborative editing tools, stock tickers, anywhere the server needs to talk first.

Quick Comparison

Style Data Format Communication Best For
REST JSON/XML Request/response General-purpose, public APIs
GraphQL JSON Request/response (flexible shape) Complex, nested, client-driven data needs
gRPC Protobuf (binary) Request/response + streaming Internal microservices, high performance
SOAP XML Request/response Enterprise, regulated industries
WebSocket Any (often JSON) Persistent, bidirectional Real-time, push-based apps

How to Choose

A few rules of thumb that tend to hold up in practice:

  • Building a public API for third parties? Start with REST. It's the lowest common denominator everyone understands.
  • Frontend team constantly needs new field combinations and you're tired of versioning endpoints? GraphQL earns its complexity.
  • Talking service-to-service inside your own infrastructure, and performance matters? gRPC.
  • Integrating with a bank, hospital system, or government agency? You'll probably be handed a WSDL file and told to use SOAP and that's fine, it's built for that world.
  • Need the server to push updates the moment something happens? WebSocket (or a managed alternative like Server-Sent Events for one-directional push).

None of these is objectively "better." They're solving for different constraints, and picking one is really about matching the constraint you actually have,public consumers, internal services, complex nested data, regulatory requirements, or real-time updates.

Wrapping up

You'll end up using more than one of these in a typical career, sometimes in the same project,REST for a public-facing API, gRPC between internal services, WebSocket for the live notification feed. Knowing why each one exists, not just the syntax, is what makes it easy to pick the right one instead of defaulting to whatever you used last time.

What do you reach for most often, and why? Curious to hear what other people are running in production.

Top comments (0)